From 6ee802c42d32347f8ed9b736ca134dccfcf4868d Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 23 Apr 2021 10:53:27 +0200 Subject: [PATCH 01/40] Add NullAway plugin. --- sentry/build.gradle.kts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sentry/build.gradle.kts b/sentry/build.gradle.kts index 2c88db38c6..069643210f 100644 --- a/sentry/build.gradle.kts +++ b/sentry/build.gradle.kts @@ -1,3 +1,4 @@ +import net.ltgt.gradle.errorprone.errorprone import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -27,6 +28,7 @@ dependencies { errorprone(Config.CompileOnly.errorprone) errorproneJavac(Config.CompileOnly.errorProneJavac8) compileOnly(Config.CompileOnly.jetbrainsAnnotations) + errorprone("com.uber.nullaway:nullaway:0.9.1") // tests testImplementation(kotlin(Config.kotlinStdLib)) @@ -79,6 +81,13 @@ buildConfig { } val generateBuildConfig by tasks -tasks.withType().configureEach { +tasks.withType() { dependsOn(generateBuildConfig) + // remove the if condition if you want to run NullAway on test code + if (!name.toLowerCase().contains("test")) { + options.errorprone { + check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) + option("NullAway:AnnotatedPackages", "io.sentry.protocol") + } + } } From 58f727a17f1b01b659814c3df6fd490d78da07d5 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 23 Apr 2021 11:08:04 +0200 Subject: [PATCH 02/40] Add nullability annotations for `App`, `Request`, `SentryId`. --- .../src/main/java/io/sentry/protocol/App.java | 46 +++++++++-------- .../main/java/io/sentry/protocol/Request.java | 50 +++++++++---------- .../java/io/sentry/protocol/SentryId.java | 8 +-- .../test/java/io/sentry/protocol/AppTest.kt | 9 +++- 4 files changed, 58 insertions(+), 55 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/App.java b/sentry/src/main/java/io/sentry/protocol/App.java index 3cc8be5077..682f280afb 100644 --- a/sentry/src/main/java/io/sentry/protocol/App.java +++ b/sentry/src/main/java/io/sentry/protocol/App.java @@ -7,92 +7,94 @@ import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; public final class App implements IUnknownPropertiesConsumer, Cloneable { public static final String TYPE = "app"; /** Version-independent application identifier, often a dotted bundle ID. */ - private String appIdentifier; + private @Nullable String appIdentifier; /** * Start time of the app. * *

Formatted UTC timestamp when the user started the application. */ - private Date appStartTime; + private @Nullable Date appStartTime; /** Application-specific device identifier. */ - private String deviceAppHash; + private @Nullable String deviceAppHash; /** String identifying the kind of build. For example, `testflight`. */ - private String buildType; + private @Nullable String buildType; /** Application name as it appears on the platform. */ - private String appName; + private @Nullable String appName; /** Application version as it appears on the platform. */ - private String appVersion; + private @Nullable String appVersion; /** Internal build ID as it appears on the platform. */ - private String appBuild; + private @Nullable String appBuild; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getAppIdentifier() { + public @Nullable String getAppIdentifier() { return appIdentifier; } - public void setAppIdentifier(String appIdentifier) { + public void setAppIdentifier(final @Nullable String appIdentifier) { this.appIdentifier = appIdentifier; } @SuppressWarnings({"JdkObsolete", "JavaUtilDate"}) - public Date getAppStartTime() { + public @Nullable Date getAppStartTime() { final Date appStartTimeRef = appStartTime; return appStartTimeRef != null ? (Date) appStartTimeRef.clone() : null; } - public void setAppStartTime(Date appStartTime) { + public void setAppStartTime(final @Nullable Date appStartTime) { this.appStartTime = appStartTime; } - public String getDeviceAppHash() { + public @Nullable String getDeviceAppHash() { return deviceAppHash; } - public void setDeviceAppHash(String deviceAppHash) { + public void setDeviceAppHash(final @Nullable String deviceAppHash) { this.deviceAppHash = deviceAppHash; } - public String getBuildType() { + public @Nullable String getBuildType() { return buildType; } - public void setBuildType(String buildType) { + public void setBuildType(final @Nullable String buildType) { this.buildType = buildType; } - public String getAppName() { + public @Nullable String getAppName() { return appName; } - public void setAppName(String appName) { + public void setAppName(final @Nullable String appName) { this.appName = appName; } - public String getAppVersion() { + public @Nullable String getAppVersion() { return appVersion; } - public void setAppVersion(String appVersion) { + public void setAppVersion(final @Nullable String appVersion) { this.appVersion = appVersion; } - public String getAppBuild() { + public @Nullable String getAppBuild() { return appBuild; } - public void setAppBuild(String appBuild) { + public void setAppBuild(final @Nullable String appBuild) { this.appBuild = appBuild; } @TestOnly + @Nullable Map getUnknown() { return unknown; } diff --git a/sentry/src/main/java/io/sentry/protocol/Request.java b/sentry/src/main/java/io/sentry/protocol/Request.java index 42b6aadb26..1998e908a4 100644 --- a/sentry/src/main/java/io/sentry/protocol/Request.java +++ b/sentry/src/main/java/io/sentry/protocol/Request.java @@ -51,9 +51,9 @@ public final class Request implements Cloneable, IUnknownPropertiesConsumer { *

The query string can be declared either as part of the `url`, or separately in * `query_string`. */ - private String url; + private @Nullable String url; /** HTTP request method. */ - private String method; + private @Nullable String method; /** * The query string component of the URL. * @@ -63,27 +63,27 @@ public final class Request implements Cloneable, IUnknownPropertiesConsumer { *

If the query string is not declared and part of the `url`, Sentry moves it to the query * string. */ - private String queryString; + private @Nullable String queryString; /** * Request data in any format that makes sense. * *

SDKs should discard large and binary bodies by default. Can be given as string or structural * data of any serializable format. */ - private Object data; + private @Nullable Object data; /** * The cookie values. * *

Can be given unparsed as string, as dictionary, or as a list of tuples. */ - private String cookies; + private @Nullable String cookies; /** * A dictionary of submitted headers. * *

If a header appears multiple times it, needs to be merged according to the HTTP standard for * header merging. Header names are treated case-insensitively by Sentry. */ - private Map headers; + private @Nullable Map headers; /** * Server environment data, such as CGI/WSGI. * @@ -92,74 +92,74 @@ public final class Request implements Cloneable, IUnknownPropertiesConsumer { * *

Sentry will explicitly look for `REMOTE_ADDR` to extract an IP address. */ - private Map env; + private @Nullable Map env; - private Map other; + private @Nullable Map other; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getUrl() { + public @Nullable String getUrl() { return url; } - public void setUrl(String url) { + public void setUrl(final @Nullable String url) { this.url = url; } - public String getMethod() { + public @Nullable String getMethod() { return method; } - public void setMethod(String method) { + public void setMethod(final @Nullable String method) { this.method = method; } - public String getQueryString() { + public @Nullable String getQueryString() { return queryString; } - public void setQueryString(String queryString) { + public void setQueryString(final @Nullable String queryString) { this.queryString = queryString; } - public Object getData() { + public @Nullable Object getData() { return data; } - public void setData(Object data) { + public void setData(final @Nullable Object data) { this.data = data; } - public String getCookies() { + public @Nullable String getCookies() { return cookies; } - public void setCookies(String cookies) { + public void setCookies(final @Nullable String cookies) { this.cookies = cookies; } - public Map getHeaders() { + public @Nullable Map getHeaders() { return headers; } - public void setHeaders(Map headers) { + public void setHeaders(final @Nullable Map headers) { this.headers = headers; } - public Map getEnvs() { + public @Nullable Map getEnvs() { return env; } - public void setEnvs(Map env) { + public void setEnvs(final @Nullable Map env) { this.env = env; } - public Map getOthers() { + public @Nullable Map getOthers() { return other; } - public void setOthers(Map other) { + public void setOthers(final @Nullable Map other) { this.other = other; } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index 7606e0d442..36d8fee8dd 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -20,7 +20,7 @@ public SentryId(@Nullable UUID uuid) { this.uuid = uuid; } - public SentryId(String sentryIdString) { + public SentryId(final @NotNull String sentryIdString) { this.uuid = fromStringSentryId(sentryIdString); } @@ -42,11 +42,7 @@ public int hashCode() { return uuid.hashCode(); } - private UUID fromStringSentryId(String sentryIdString) { - if (sentryIdString == null) { - return null; - } - + private @NotNull UUID fromStringSentryId(@NotNull String sentryIdString) { if (sentryIdString.length() == 32) { // expected format, SentryId is a UUID without dashes sentryIdString = diff --git a/sentry/src/test/java/io/sentry/protocol/AppTest.kt b/sentry/src/test/java/io/sentry/protocol/AppTest.kt index 0fb672764c..95e021add0 100644 --- a/sentry/src/test/java/io/sentry/protocol/AppTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/AppTest.kt @@ -48,10 +48,15 @@ class AppTest { assertEquals("app build", clone.appBuild) assertEquals("app identifier", clone.appIdentifier) assertEquals("app name", clone.appName) - assertEquals(date.time, clone.appStartTime.time) + assertNotNull(clone.appStartTime) { + assertEquals(date.time, it.time) + } assertEquals("app version", clone.appVersion) assertEquals("build type", clone.buildType) assertEquals("device app hash", clone.deviceAppHash) - assertEquals("unknown", clone.unknown["unknown"]) + assertNotNull(clone.unknown) { + assertEquals("unknown", it["unknown"]) + } + } } From 4810f2454474ce964c3640af2aaace35798555de Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 23 Apr 2021 11:38:11 +0200 Subject: [PATCH 03/40] Add nullability annotations for `Browser`, `Device`, `OperatingSystem`. --- .../main/java/io/sentry/protocol/Browser.java | 16 +- .../main/java/io/sentry/protocol/Device.java | 188 +++++++++--------- .../io/sentry/protocol/OperatingSystem.java | 42 ++-- .../test/java/io/sentry/GsonSerializerTest.kt | 2 +- .../java/io/sentry/protocol/BrowserTest.kt | 4 +- .../java/io/sentry/protocol/DeviceTest.kt | 12 +- .../io/sentry/protocol/OperatingSystemTest.kt | 4 +- 7 files changed, 141 insertions(+), 127 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/Browser.java b/sentry/src/main/java/io/sentry/protocol/Browser.java index c15d0547ba..7c8f01a7b9 100644 --- a/sentry/src/main/java/io/sentry/protocol/Browser.java +++ b/sentry/src/main/java/io/sentry/protocol/Browser.java @@ -6,35 +6,37 @@ import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; public final class Browser implements IUnknownPropertiesConsumer, Cloneable { public static final String TYPE = "browser"; /** Display name of the browser application. */ - private String name; + private @Nullable String name; /** Version string of the browser. */ - private String version; + private @Nullable String version; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getName() { + public @Nullable String getName() { return name; } - public void setName(String name) { + public void setName(final @Nullable String name) { this.name = name; } - public String getVersion() { + public @Nullable String getVersion() { return version; } - public void setVersion(String version) { + public void setVersion(final @Nullable String version) { this.version = version; } @TestOnly + @Nullable Map getUnknown() { return unknown; } diff --git a/sentry/src/main/java/io/sentry/protocol/Device.java b/sentry/src/main/java/io/sentry/protocol/Device.java index e322b346c3..2e6312adbe 100644 --- a/sentry/src/main/java/io/sentry/protocol/Device.java +++ b/sentry/src/main/java/io/sentry/protocol/Device.java @@ -8,342 +8,344 @@ import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; public final class Device implements IUnknownPropertiesConsumer, Cloneable { public static final String TYPE = "device"; /** Name of the device. */ - private String name; + private @Nullable String name; /** Manufacturer of the device. */ - private String manufacturer; + private @Nullable String manufacturer; /** Brand of the device. */ - private String brand; + private @Nullable String brand; /** * Family of the device model. * *

This is usually the common part of model names across generations. For instance, `iPhone` * would be a reasonable family, so would be `Samsung Galaxy`. */ - private String family; + private @Nullable String family; /** * Device model. * *

This, for example, can be `Samsung Galaxy S3`. */ - private String model; + private @Nullable String model; /** * Device model (internal identifier). * *

An internal hardware revision to identify the device exactly. */ - private String modelId; + private @Nullable String modelId; /** Supported CPU architectures of the device. */ - private String[] archs; + private @Nullable String[] archs; /** * Current battery level in %. * *

If the device has a battery, this can be a floating point value defining the battery level * (in the range 0-100). */ - private Float batteryLevel; + private @Nullable Float batteryLevel; /** Whether the device was charging or not. */ - private Boolean charging; + private @Nullable Boolean charging; /** Whether the device was online or not. */ - private Boolean online; + private @Nullable Boolean online; /** * Current screen orientation. * *

This can be a string `portrait` or `landscape` to define the orientation of a device. */ - private DeviceOrientation orientation; + private @Nullable DeviceOrientation orientation; /** Simulator/prod indicator. */ - private Boolean simulator; + private @Nullable Boolean simulator; /** Total memory available in bytes. */ - private Long memorySize; + private @Nullable Long memorySize; /** How much memory is still available in bytes. */ - private Long freeMemory; + private @Nullable Long freeMemory; /** How much memory is usable for the app in bytes. */ - private Long usableMemory; + private @Nullable Long usableMemory; /** Whether the device was low on memory. */ - private Boolean lowMemory; + private @Nullable Boolean lowMemory; /** Total storage size of the device in bytes. */ - private Long storageSize; + private @Nullable Long storageSize; /** How much storage is free in bytes. */ - private Long freeStorage; + private @Nullable Long freeStorage; /** Total size of the attached external storage in bytes (eg: android SDK card). */ - private Long externalStorageSize; + private @Nullable Long externalStorageSize; /** Free size of the attached external storage in bytes (eg: android SDK card). */ - private Long externalFreeStorage; + private @Nullable Long externalFreeStorage; /** Device width screen resolution. */ - private Integer screenWidthPixels; + private @Nullable Integer screenWidthPixels; /** Device Height screen resolution. */ - private Integer screenHeightPixels; + private @Nullable Integer screenHeightPixels; /** Device screen density. */ - private Float screenDensity; + private @Nullable Float screenDensity; /** Screen density as dots-per-inch. */ - private Integer screenDpi; + private @Nullable Integer screenDpi; /** Indicator when the device was booted. */ - private Date bootTime; + private @Nullable Date bootTime; /** Timezone of the device. */ - private TimeZone timezone; + private @Nullable TimeZone timezone; - private String id; - private String language; - private String connectionType; + private @Nullable String id; + private @Nullable String language; + private @Nullable String connectionType; /** battery's temperature in celsius */ - private Float batteryTemperature; + private @Nullable Float batteryTemperature; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getName() { + public @Nullable String getName() { return name; } - public void setName(String name) { + public void setName(final @Nullable String name) { this.name = name; } - public String getManufacturer() { + public @Nullable String getManufacturer() { return manufacturer; } - public void setManufacturer(String manufacturer) { + public void setManufacturer(final @Nullable String manufacturer) { this.manufacturer = manufacturer; } - public String getBrand() { + public @Nullable String getBrand() { return brand; } - public void setBrand(String brand) { + public void setBrand(final @Nullable String brand) { this.brand = brand; } - public String getFamily() { + public @Nullable String getFamily() { return family; } - public void setFamily(String family) { + public void setFamily(final @Nullable String family) { this.family = family; } - public String getModel() { + public @Nullable String getModel() { return model; } - public void setModel(String model) { + public void setModel(final @Nullable String model) { this.model = model; } - public String getModelId() { + public @Nullable String getModelId() { return modelId; } - public void setModelId(String modelId) { + public void setModelId(final @Nullable String modelId) { this.modelId = modelId; } - public Float getBatteryLevel() { + public @Nullable Float getBatteryLevel() { return batteryLevel; } - public void setBatteryLevel(Float batteryLevel) { + public void setBatteryLevel(final @Nullable Float batteryLevel) { this.batteryLevel = batteryLevel; } - public Boolean isCharging() { + public @Nullable Boolean isCharging() { return charging; } - public void setCharging(Boolean charging) { + public void setCharging(final @Nullable Boolean charging) { this.charging = charging; } - public Boolean isOnline() { + public @Nullable Boolean isOnline() { return online; } - public void setOnline(Boolean online) { + public void setOnline(final @Nullable Boolean online) { this.online = online; } - public DeviceOrientation getOrientation() { + public @Nullable DeviceOrientation getOrientation() { return orientation; } - public void setOrientation(DeviceOrientation orientation) { + public void setOrientation(final @Nullable DeviceOrientation orientation) { this.orientation = orientation; } - public Boolean isSimulator() { + public @Nullable Boolean isSimulator() { return simulator; } - public void setSimulator(Boolean simulator) { + public void setSimulator(final @Nullable Boolean simulator) { this.simulator = simulator; } - public Long getMemorySize() { + public @Nullable Long getMemorySize() { return memorySize; } - public void setMemorySize(Long memorySize) { + public void setMemorySize(final @Nullable Long memorySize) { this.memorySize = memorySize; } - public Long getFreeMemory() { + public @Nullable Long getFreeMemory() { return freeMemory; } - public void setFreeMemory(Long freeMemory) { + public void setFreeMemory(final @Nullable Long freeMemory) { this.freeMemory = freeMemory; } - public Long getUsableMemory() { + public @Nullable Long getUsableMemory() { return usableMemory; } - public void setUsableMemory(Long usableMemory) { + public void setUsableMemory(final @Nullable Long usableMemory) { this.usableMemory = usableMemory; } - public Boolean isLowMemory() { + public @Nullable Boolean isLowMemory() { return lowMemory; } - public void setLowMemory(Boolean lowMemory) { + public void setLowMemory(final @Nullable Boolean lowMemory) { this.lowMemory = lowMemory; } - public Long getStorageSize() { + public @Nullable Long getStorageSize() { return storageSize; } - public void setStorageSize(Long storageSize) { + public void setStorageSize(final @Nullable Long storageSize) { this.storageSize = storageSize; } - public Long getFreeStorage() { + public @Nullable Long getFreeStorage() { return freeStorage; } - public void setFreeStorage(Long freeStorage) { + public void setFreeStorage(final @Nullable Long freeStorage) { this.freeStorage = freeStorage; } - public Long getExternalStorageSize() { + public @Nullable Long getExternalStorageSize() { return externalStorageSize; } - public void setExternalStorageSize(Long externalStorageSize) { + public void setExternalStorageSize(final @Nullable Long externalStorageSize) { this.externalStorageSize = externalStorageSize; } - public Long getExternalFreeStorage() { + public @Nullable Long getExternalFreeStorage() { return externalFreeStorage; } - public void setExternalFreeStorage(Long externalFreeStorage) { + public void setExternalFreeStorage(final @Nullable Long externalFreeStorage) { this.externalFreeStorage = externalFreeStorage; } - public Float getScreenDensity() { + public @Nullable Float getScreenDensity() { return screenDensity; } - public void setScreenDensity(Float screenDensity) { + public void setScreenDensity(final @Nullable Float screenDensity) { this.screenDensity = screenDensity; } - public Integer getScreenDpi() { + public @Nullable Integer getScreenDpi() { return screenDpi; } - public void setScreenDpi(Integer screenDpi) { + public void setScreenDpi(final @Nullable Integer screenDpi) { this.screenDpi = screenDpi; } @SuppressWarnings({"JdkObsolete", "JavaUtilDate"}) - public Date getBootTime() { + public @Nullable Date getBootTime() { final Date bootTimeRef = bootTime; return bootTimeRef != null ? (Date) bootTimeRef.clone() : null; } - public void setBootTime(Date bootTime) { + public void setBootTime(final @Nullable Date bootTime) { this.bootTime = bootTime; } - public TimeZone getTimezone() { + public @Nullable TimeZone getTimezone() { return timezone; } - public void setTimezone(TimeZone timezone) { + public void setTimezone(final @Nullable TimeZone timezone) { this.timezone = timezone; } - public String[] getArchs() { + public @Nullable String[] getArchs() { return archs; } - public void setArchs(String[] archs) { + public void setArchs(final @Nullable String[] archs) { this.archs = archs; } - public Integer getScreenWidthPixels() { + public @Nullable Integer getScreenWidthPixels() { return screenWidthPixels; } - public void setScreenWidthPixels(Integer screenWidthPixels) { + public void setScreenWidthPixels(final @Nullable Integer screenWidthPixels) { this.screenWidthPixels = screenWidthPixels; } - public Integer getScreenHeightPixels() { + public @Nullable Integer getScreenHeightPixels() { return screenHeightPixels; } - public void setScreenHeightPixels(Integer screenHeightPixels) { + public void setScreenHeightPixels(final @Nullable Integer screenHeightPixels) { this.screenHeightPixels = screenHeightPixels; } - public String getId() { + public @Nullable String getId() { return id; } - public void setId(String id) { + public void setId(final @Nullable String id) { this.id = id; } - public String getLanguage() { + public @Nullable String getLanguage() { return language; } - public void setLanguage(String language) { + public void setLanguage(final @Nullable String language) { this.language = language; } - public String getConnectionType() { + public @Nullable String getConnectionType() { return connectionType; } - public void setConnectionType(String connectionType) { + public void setConnectionType(final @Nullable String connectionType) { this.connectionType = connectionType; } - public Float getBatteryTemperature() { + public @Nullable Float getBatteryTemperature() { return batteryTemperature; } - public void setBatteryTemperature(Float batteryTemperature) { + public void setBatteryTemperature(final @Nullable Float batteryTemperature) { this.batteryTemperature = batteryTemperature; } @TestOnly + @Nullable Map getUnknown() { return unknown; } @@ -365,10 +367,10 @@ public void acceptUnknownProperties(Map unknown) { final Device clone = (Device) super.clone(); final String[] archsRef = this.archs; - clone.archs = archsRef != null ? this.archs.clone() : null; + clone.archs = archsRef != null ? archsRef.clone() : null; final TimeZone timezoneRef = this.timezone; - clone.timezone = timezoneRef != null ? (TimeZone) this.timezone.clone() : null; + clone.timezone = timezoneRef != null ? (TimeZone) timezoneRef.clone() : null; clone.unknown = CollectionUtils.shallowCopy(unknown); diff --git a/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java b/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java index 7b65466c76..fcc9442986 100644 --- a/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java +++ b/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java @@ -6,15 +6,16 @@ import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; public final class OperatingSystem implements IUnknownPropertiesConsumer, Cloneable { public static final String TYPE = "os"; /** Name of the operating system. */ - private String name; + private @Nullable String name; /** Version of the operating system. */ - private String version; + private @Nullable String version; /** * Unprocessed operating system info. * @@ -22,77 +23,78 @@ public final class OperatingSystem implements IUnknownPropertiesConsumer, Clonea * runtimes, Sentry will attempt to parse `name` and `version` from this string, if they are not * explicitly given. */ - private String rawDescription; + private @Nullable String rawDescription; /** Internal build number of the operating system. */ - private String build; + private @Nullable String build; /** * Current kernel version. * *

This is typically the entire output of the `uname` syscall. */ - private String kernelVersion; + private @Nullable String kernelVersion; /** Indicator if the OS is rooted (mobile mostly). */ - private Boolean rooted; + private @Nullable Boolean rooted; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getName() { + public @Nullable String getName() { return name; } - public void setName(String name) { + public void setName(final @Nullable String name) { this.name = name; } - public String getVersion() { + public @Nullable String getVersion() { return version; } - public void setVersion(String version) { + public void setVersion(final @Nullable String version) { this.version = version; } - public String getRawDescription() { + public @Nullable String getRawDescription() { return rawDescription; } - public void setRawDescription(String rawDescription) { + public void setRawDescription(final @Nullable String rawDescription) { this.rawDescription = rawDescription; } - public String getBuild() { + public @Nullable String getBuild() { return build; } - public void setBuild(String build) { + public void setBuild(final @Nullable String build) { this.build = build; } - public String getKernelVersion() { + public @Nullable String getKernelVersion() { return kernelVersion; } - public void setKernelVersion(String kernelVersion) { + public void setKernelVersion(final @Nullable String kernelVersion) { this.kernelVersion = kernelVersion; } - public Boolean isRooted() { + public @Nullable Boolean isRooted() { return rooted; } - public void setRooted(Boolean rooted) { + public void setRooted(final @Nullable Boolean rooted) { this.rooted = rooted; } @TestOnly + @Nullable Map getUnknown() { return unknown; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/test/java/io/sentry/GsonSerializerTest.kt b/sentry/src/test/java/io/sentry/GsonSerializerTest.kt index 1b4e59f583..09ac3c52f1 100644 --- a/sentry/src/test/java/io/sentry/GsonSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/GsonSerializerTest.kt @@ -235,7 +235,7 @@ class GsonSerializerTest { val actual = fixture.serializer.deserialize(StringReader(jsonEvent), SentryEvent::class.java) - assertEquals("Europe/Vienna", actual!!.contexts.device!!.timezone.id) + assertEquals("Europe/Vienna", actual!!.contexts.device!!.timezone!!.id) } @Test diff --git a/sentry/src/test/java/io/sentry/protocol/BrowserTest.kt b/sentry/src/test/java/io/sentry/protocol/BrowserTest.kt index feabd36e01..2005e8b9e2 100644 --- a/sentry/src/test/java/io/sentry/protocol/BrowserTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/BrowserTest.kt @@ -33,6 +33,8 @@ class BrowserTest { assertEquals("browser name", clone.name) assertEquals("browser version", clone.version) - assertEquals("unknown", clone.unknown["unknown"]) + assertNotNull(clone.unknown) { + assertEquals("unknown", it["unknown"]) + } } } diff --git a/sentry/src/test/java/io/sentry/protocol/DeviceTest.kt b/sentry/src/test/java/io/sentry/protocol/DeviceTest.kt index 2542f3221f..72a5c0527e 100644 --- a/sentry/src/test/java/io/sentry/protocol/DeviceTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/DeviceTest.kt @@ -71,9 +71,11 @@ class DeviceTest { assertEquals("family", clone.family) assertEquals("model", clone.model) assertEquals("modelId", clone.modelId) - assertEquals(2, clone.archs.size) - assertEquals("archs1", clone.archs[0]) - assertEquals("archs2", clone.archs[1]) + assertNotNull(clone.archs) { + assertEquals(2, it.size) + assertEquals("archs1", it[0]) + assertEquals("archs2", it[1]) + } assertEquals(3.14f, clone.batteryLevel) assertEquals(true, clone.isCharging) assertEquals(true, clone.isOnline) @@ -95,6 +97,8 @@ class DeviceTest { assertEquals("language", clone.language) assertEquals("connection type", clone.connectionType) assertEquals(30f, clone.batteryTemperature) - assertEquals("unknown", clone.unknown["unknown"]) + assertNotNull(clone.unknown) { + assertEquals("unknown", it["unknown"]) + } } } diff --git a/sentry/src/test/java/io/sentry/protocol/OperatingSystemTest.kt b/sentry/src/test/java/io/sentry/protocol/OperatingSystemTest.kt index a59b3f43c1..a05e3636cc 100644 --- a/sentry/src/test/java/io/sentry/protocol/OperatingSystemTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/OperatingSystemTest.kt @@ -40,6 +40,8 @@ class OperatingSystemTest { assertEquals("build", clone.build) assertEquals("kernel version", clone.kernelVersion) assertEquals(true, clone.isRooted) - assertEquals("unknown", clone.unknown["unknown"]) + assertNotNull(clone.unknown) { + assertEquals("unknown", it["unknown"]) + } } } From bc25307f9dac5194baba3e0a38960823d9a9053d Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 23 Apr 2021 11:39:21 +0200 Subject: [PATCH 04/40] Refactor --- buildSrc/src/main/java/Config.kt | 1 + sentry/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index e0eecc32ed..c346862f3e 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -143,6 +143,7 @@ object Config { val nopenChecker = "com.jakewharton.nopen:nopen-checker:$nopenVersion" val errorprone = "com.google.errorprone:error_prone_core:2.5.1" val errorProneJavac8 = "com.google.errorprone:javac:9+181-r4173-1" + val errorProneNullAway = "com.uber.nullaway:nullaway:0.9.1" } object NativePlugins { diff --git a/sentry/build.gradle.kts b/sentry/build.gradle.kts index 069643210f..a22aab8197 100644 --- a/sentry/build.gradle.kts +++ b/sentry/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { errorprone(Config.CompileOnly.errorprone) errorproneJavac(Config.CompileOnly.errorProneJavac8) compileOnly(Config.CompileOnly.jetbrainsAnnotations) - errorprone("com.uber.nullaway:nullaway:0.9.1") + errorprone(Config.CompileOnly.errorProneNullAway) // tests testImplementation(kotlin(Config.kotlinStdLib)) From fed90f603896e00b0801c0637ae6a73ee29a0ece Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 23 Apr 2021 12:00:45 +0200 Subject: [PATCH 05/40] Fix tests. --- ...SentryRequestHttpServletRequestProcessorTest.kt | 14 ++++++++------ .../spring/SentrySpringRequestListenerTest.kt | 14 ++++++++------ sentry/src/test/java/io/sentry/protocol/AppTest.kt | 1 - 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryRequestHttpServletRequestProcessorTest.kt b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryRequestHttpServletRequestProcessorTest.kt index d18614892a..0402535582 100644 --- a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryRequestHttpServletRequestProcessorTest.kt +++ b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryRequestHttpServletRequestProcessorTest.kt @@ -91,12 +91,14 @@ class SentryRequestHttpServletRequestProcessorTest { eventProcessor.process(event, null) - assertNotNull(event.request) { - assertFalse(it.headers.containsKey("X-FORWARDED-FOR")) - assertFalse(it.headers.containsKey("Authorization")) - assertFalse(it.headers.containsKey("authorization")) - assertFalse(it.headers.containsKey("Cookies")) - assertTrue(it.headers.containsKey("some-header")) + assertNotNull(event.request) { request -> + assertNotNull(request.headers) { + assertFalse(it.containsKey("X-FORWARDED-FOR")) + assertFalse(it.containsKey("Authorization")) + assertFalse(it.containsKey("authorization")) + assertFalse(it.containsKey("Cookies")) + assertTrue(it.containsKey("some-header")) + } } } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringRequestListenerTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringRequestListenerTest.kt index 34ac7a1072..ec8704516b 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringRequestListenerTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringRequestListenerTest.kt @@ -169,12 +169,14 @@ class SentrySpringRequestListenerTest { listener.requestInitialized(fixture.event) - assertNotNull(fixture.scope.request) { - assertFalse(it.headers.containsKey("X-FORWARDED-FOR")) - assertFalse(it.headers.containsKey("Authorization")) - assertFalse(it.headers.containsKey("authorization")) - assertFalse(it.headers.containsKey("Cookie")) - assertTrue(it.headers.containsKey("some-header")) + assertNotNull(fixture.scope.request) { request -> + assertNotNull(request.headers) { + assertFalse(it.containsKey("X-FORWARDED-FOR")) + assertFalse(it.containsKey("Authorization")) + assertFalse(it.containsKey("authorization")) + assertFalse(it.containsKey("Cookie")) + assertTrue(it.containsKey("some-header")) + } } } } diff --git a/sentry/src/test/java/io/sentry/protocol/AppTest.kt b/sentry/src/test/java/io/sentry/protocol/AppTest.kt index 95e021add0..cf8145ae26 100644 --- a/sentry/src/test/java/io/sentry/protocol/AppTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/AppTest.kt @@ -57,6 +57,5 @@ class AppTest { assertNotNull(clone.unknown) { assertEquals("unknown", it["unknown"]) } - } } From fff6e0dd509d21ad38d452cc52d432ea384a5149 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 23 Apr 2021 14:16:14 +0200 Subject: [PATCH 06/40] Add nullability annotations for `Gpu`, `SentryRuntime`, `SentrySpan`. --- .../src/main/java/io/sentry/protocol/Gpu.java | 54 ++++++++++--------- .../io/sentry/protocol/SentryRuntime.java | 24 +++++---- .../java/io/sentry/protocol/SentrySpan.java | 2 +- .../test/java/io/sentry/protocol/GpuTest.kt | 4 +- .../io/sentry/protocol/SentryRuntimeTest.kt | 4 +- 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/sentry/src/main/java/io/sentry/protocol/Gpu.java b/sentry/src/main/java/io/sentry/protocol/Gpu.java index d6067c5812..69af5587b3 100644 --- a/sentry/src/main/java/io/sentry/protocol/Gpu.java +++ b/sentry/src/main/java/io/sentry/protocol/Gpu.java @@ -6,38 +6,39 @@ import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; public final class Gpu implements IUnknownPropertiesConsumer, Cloneable { public static final String TYPE = "gpu"; /** The name of the graphics device. */ - private String name; + private @Nullable String name; /** The PCI identifier of the graphics device. */ - private Integer id; + private @Nullable Integer id; /** The PCI vendor identifier of the graphics device. */ - private Integer vendorId; + private @Nullable Integer vendorId; /** The vendor name as reported by the graphics device. */ - private String vendorName; + private @Nullable String vendorName; /** The total GPU memory available in Megabytes. */ - private Integer memorySize; + private @Nullable Integer memorySize; /** * The device low-level API type. * *

Examples: `"Apple Metal"` or `"Direct3D11"` */ - private String apiType; + private @Nullable String apiType; /** Whether the GPU has multi-threaded rendering or not. */ - private Boolean multiThreadedRendering; + private @Nullable Boolean multiThreadedRendering; /** The Version of the graphics device. */ - private String version; + private @Nullable String version; /** The Non-Power-Of-Two support. */ - private String npotSupport; + private @Nullable String npotSupport; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getName() { + public @Nullable String getName() { return name; } @@ -45,7 +46,7 @@ public void setName(String name) { this.name = name; } - public Integer getId() { + public @Nullable Integer getId() { return id; } @@ -53,7 +54,7 @@ public void setId(Integer id) { this.id = id; } - public Integer getVendorId() { + public @Nullable Integer getVendorId() { return vendorId; } @@ -61,62 +62,63 @@ public void setVendorId(Integer vendorId) { this.vendorId = vendorId; } - public String getVendorName() { + public @Nullable String getVendorName() { return vendorName; } - public void setVendorName(String vendorName) { + public void setVendorName(final @Nullable String vendorName) { this.vendorName = vendorName; } - public Integer getMemorySize() { + public @Nullable Integer getMemorySize() { return memorySize; } - public void setMemorySize(Integer memorySize) { + public void setMemorySize(final @Nullable Integer memorySize) { this.memorySize = memorySize; } - public String getApiType() { + public @Nullable String getApiType() { return apiType; } - public void setApiType(String apiType) { + public void setApiType(final @Nullable String apiType) { this.apiType = apiType; } - public Boolean isMultiThreadedRendering() { + public @Nullable Boolean isMultiThreadedRendering() { return multiThreadedRendering; } - public void setMultiThreadedRendering(Boolean multiThreadedRendering) { + public void setMultiThreadedRendering(final @Nullable Boolean multiThreadedRendering) { this.multiThreadedRendering = multiThreadedRendering; } - public String getVersion() { + public @Nullable String getVersion() { return version; } - public void setVersion(String version) { + public void setVersion(final @Nullable String version) { this.version = version; } - public String getNpotSupport() { + public @Nullable String getNpotSupport() { return npotSupport; } - public void setNpotSupport(String npotSupport) { + public void setNpotSupport(final @Nullable String npotSupport) { this.npotSupport = npotSupport; } @TestOnly + @Nullable Map getUnknown() { return unknown; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java b/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java index d83f1dc750..d22b652f20 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java @@ -6,15 +6,16 @@ import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; public final class SentryRuntime implements IUnknownPropertiesConsumer, Cloneable { public static final String TYPE = "runtime"; /** Runtime name. */ - private String name; + private @Nullable String name; /** Runtime version string. */ - private String version; + private @Nullable String version; /** * Unprocessed runtime info. * @@ -22,43 +23,44 @@ public final class SentryRuntime implements IUnknownPropertiesConsumer, Cloneabl * Sentry will attempt to parse `name` and `version` from this string, if they are not explicitly * given. */ - private String rawDescription; + private @Nullable String rawDescription; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getName() { + public @Nullable String getName() { return name; } - public void setName(String name) { + public void setName(final @Nullable String name) { this.name = name; } - public String getVersion() { + public @Nullable String getVersion() { return version; } - public void setVersion(String version) { + public void setVersion(final @Nullable String version) { this.version = version; } - public String getRawDescription() { + public @Nullable String getRawDescription() { return rawDescription; } - public void setRawDescription(String rawDescription) { + public void setRawDescription(final @Nullable String rawDescription) { this.rawDescription = rawDescription; } @TestOnly + @Nullable Map getUnknown() { return unknown; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/SentrySpan.java b/sentry/src/main/java/io/sentry/protocol/SentrySpan.java index 88970701ee..80b6ead4d0 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentrySpan.java +++ b/sentry/src/main/java/io/sentry/protocol/SentrySpan.java @@ -56,7 +56,7 @@ public boolean isFinished() { return spanId; } - public @NotNull SpanId getParentSpanId() { + public @Nullable SpanId getParentSpanId() { return parentSpanId; } diff --git a/sentry/src/test/java/io/sentry/protocol/GpuTest.kt b/sentry/src/test/java/io/sentry/protocol/GpuTest.kt index 9614111cc2..90d3bb5ae1 100644 --- a/sentry/src/test/java/io/sentry/protocol/GpuTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/GpuTest.kt @@ -46,6 +46,8 @@ class GpuTest { assertEquals(true, clone.isMultiThreadedRendering) assertEquals("version", clone.version) assertEquals("npot support", clone.npotSupport) - assertEquals("unknown", clone.unknown["unknown"]) + assertNotNull(clone.unknown) { + assertEquals("unknown", it["unknown"]) + } } } diff --git a/sentry/src/test/java/io/sentry/protocol/SentryRuntimeTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryRuntimeTest.kt index 2389fc843c..5485352270 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryRuntimeTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryRuntimeTest.kt @@ -34,6 +34,8 @@ class SentryRuntimeTest { assertEquals("name", clone.name) assertEquals("version", clone.version) assertEquals("raw description", clone.rawDescription) - assertEquals("unknown", clone.unknown["unknown"]) + assertNotNull(clone.unknown) { + assertEquals("unknown", it["unknown"]) + } } } From d41f15fffa5e8f9ba954d0ea72b4fb1a2e74cfc7 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 26 Apr 2021 12:59:43 +0200 Subject: [PATCH 07/40] Add nullability annotations for `Message`, `SentryThread`. --- .../core/DefaultAndroidEventProcessorTest.kt | 4 +- .../main/java/io/sentry/protocol/Message.java | 24 +++++---- .../java/io/sentry/protocol/SentryThread.java | 54 ++++++++++--------- .../java/io/sentry/MainEventProcessorTest.kt | 12 +++-- .../java/io/sentry/SentryThreadFactoryTest.kt | 10 ++-- 5 files changed, 55 insertions(+), 49 deletions(-) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt index a88d1452e5..91acc9d68d 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt @@ -150,7 +150,7 @@ class DefaultAndroidEventProcessorTest { } // refactor and mock data later on event = sut.process(event, null) - assertTrue(event.threads.first().isCurrent) + assertTrue(event.threads.first().isCurrent == true) } @Test @@ -165,7 +165,7 @@ class DefaultAndroidEventProcessorTest { } // refactor and mock data later on event = sut.process(event, null) - assertFalse(event.threads.first().isCurrent) + assertFalse(event.threads.first().isCurrent == true) } @Test diff --git a/sentry/src/main/java/io/sentry/protocol/Message.java b/sentry/src/main/java/io/sentry/protocol/Message.java index ad07b9e08d..771b18d3cb 100644 --- a/sentry/src/main/java/io/sentry/protocol/Message.java +++ b/sentry/src/main/java/io/sentry/protocol/Message.java @@ -4,6 +4,8 @@ import java.util.List; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; // https://docs.sentry.io/development/sdk-dev/event-payloads/message/ @@ -26,7 +28,7 @@ public final class Message implements IUnknownPropertiesConsumer { * *

It must not exceed 8192 characters. Longer messages will be truncated. */ - private String formatted; + private @Nullable String formatted; /** * The log message with parameter placeholders. * @@ -36,17 +38,17 @@ public final class Message implements IUnknownPropertiesConsumer { * *

It must not exceed 8192 characters. Longer messages will be truncated. */ - private String message; + private @Nullable String message; /** * Parameters to be interpolated into the log message. This can be an array of positional * parameters as well as a mapping of named arguments to their values. */ - private List params; + private @Nullable List params; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getFormatted() { + public @Nullable String getFormatted() { return formatted; } @@ -55,29 +57,29 @@ public String getFormatted() { * * @param formatted a formatted String */ - public void setFormatted(String formatted) { + public void setFormatted(final @Nullable String formatted) { this.formatted = formatted; } - public String getMessage() { + public @Nullable String getMessage() { return message; } - public void setMessage(String message) { + public void setMessage(final @Nullable String message) { this.message = message; } - public List getParams() { + public @Nullable List getParams() { return params; } - public void setParams(List params) { + public void setParams(final @Nullable List params) { this.params = params; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryThread.java b/sentry/src/main/java/io/sentry/protocol/SentryThread.java index 04d48d0fca..a623e7fa11 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryThread.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryThread.java @@ -3,6 +3,8 @@ import io.sentry.IUnknownPropertiesConsumer; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * A process thread of an event. @@ -19,24 +21,24 @@ * {} } ] } } ``` */ public final class SentryThread implements IUnknownPropertiesConsumer { - private Long id; - private Integer priority; - private String name; - private String state; - private Boolean crashed; - private Boolean current; - private Boolean daemon; - private SentryStackTrace stacktrace; + private @Nullable Long id; + private @Nullable Integer priority; + private @Nullable String name; + private @Nullable String state; + private @Nullable Boolean crashed; + private @Nullable Boolean current; + private @Nullable Boolean daemon; + private @Nullable SentryStackTrace stacktrace; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; /** * Gets the Id of the thread. * * @return the thread id. */ - public Long getId() { + public @Nullable Long getId() { return id; } @@ -45,7 +47,7 @@ public Long getId() { * * @param id the thread id. */ - public void setId(Long id) { + public void setId(final @Nullable Long id) { this.id = id; } @@ -54,7 +56,7 @@ public void setId(Long id) { * * @return the name of the thread. */ - public String getName() { + public @Nullable String getName() { return name; } @@ -63,7 +65,7 @@ public String getName() { * * @param name the name of the thread. */ - public void setName(String name) { + public void setName(final @Nullable String name) { this.name = name; } @@ -72,7 +74,7 @@ public void setName(String name) { * * @return whether it was the crashed thread. */ - public Boolean isCrashed() { + public @Nullable Boolean isCrashed() { return crashed; } @@ -81,7 +83,7 @@ public Boolean isCrashed() { * * @param crashed whether it was the crashed thread. */ - public void setCrashed(Boolean crashed) { + public void setCrashed(final @Nullable Boolean crashed) { this.crashed = crashed; } @@ -90,7 +92,7 @@ public void setCrashed(Boolean crashed) { * * @return whether the thread was in the foreground. */ - public Boolean isCurrent() { + public @Nullable Boolean isCurrent() { return current; } @@ -99,7 +101,7 @@ public Boolean isCurrent() { * * @param current whether the thread was in the foreground. */ - public void setCurrent(Boolean current) { + public void setCurrent(final @Nullable Boolean current) { this.current = current; } @@ -108,7 +110,7 @@ public void setCurrent(Boolean current) { * * @return the thread stacktrace. */ - public SentryStackTrace getStacktrace() { + public @Nullable SentryStackTrace getStacktrace() { return stacktrace; } @@ -117,7 +119,7 @@ public SentryStackTrace getStacktrace() { * * @param stacktrace the thread stacktrace. */ - public void setStacktrace(SentryStackTrace stacktrace) { + public void setStacktrace(final @Nullable SentryStackTrace stacktrace) { this.stacktrace = stacktrace; } @@ -126,7 +128,7 @@ public void setStacktrace(SentryStackTrace stacktrace) { * * @return the thread priority. */ - public Integer getPriority() { + public @Nullable Integer getPriority() { return priority; } @@ -135,7 +137,7 @@ public Integer getPriority() { * * @param priority of the thread. */ - public void setPriority(Integer priority) { + public void setPriority(final @Nullable Integer priority) { this.priority = priority; } @@ -144,7 +146,7 @@ public void setPriority(Integer priority) { * * @return if this is a daemon thread. */ - public Boolean isDaemon() { + public @Nullable Boolean isDaemon() { return daemon; } @@ -153,7 +155,7 @@ public Boolean isDaemon() { * * @param daemon true if the thread is daemon thread. Otherwise false. */ - public void setDaemon(Boolean daemon) { + public void setDaemon(final @Nullable Boolean daemon) { this.daemon = daemon; } @@ -162,7 +164,7 @@ public void setDaemon(Boolean daemon) { * * @return the state of the thread. */ - public String getState() { + public @Nullable String getState() { return state; } @@ -171,13 +173,13 @@ public String getState() { * * @param state the state of the thread. */ - public void setState(String state) { + public void setState(final @Nullable String state) { this.state = state; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } } diff --git a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt index b10da81298..b39aeee21d 100644 --- a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt @@ -64,7 +64,9 @@ class MainEventProcessorTest { event = sut.process(event, null) assertSame(crashedThread.id, event.exceptions.first().threadId) - assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed) + assertNotNull(event.threads) { + assertTrue(it.first { t -> t.id == crashedThread.id }.isCrashed == true) + } assertFalse(event.exceptions.first().mechanism.isHandled) } @@ -76,7 +78,7 @@ class MainEventProcessorTest { var event = generateCrashedEvent(crashedThread) event = sut.process(event, null) - assertTrue(event.threads.any { it.isCrashed }) + assertTrue(event.threads.any { it.isCrashed == true }) } @Test @@ -90,7 +92,7 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed) + assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed == true) } @Test @@ -104,7 +106,7 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed) + assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed == true) } @Test @@ -149,7 +151,7 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed) + assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed == true) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryThreadFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryThreadFactoryTest.kt index 77fdc0ea38..1e8bc88683 100644 --- a/sentry/src/test/java/io/sentry/SentryThreadFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryThreadFactoryTest.kt @@ -29,7 +29,7 @@ class SentryThreadFactoryTest { @Test fun `when currentThreads is called, current thread is marked crashed`() { val sut = fixture.getSut() - assertEquals(1, sut.getCurrentThreads(null)!!.filter { it.isCrashed }.count()) + assertEquals(1, sut.getCurrentThreads(null)!!.filter { it.isCrashed == true }.count()) } @Test @@ -41,19 +41,19 @@ class SentryThreadFactoryTest { @Test fun `when currentThreads is called, some thread stack frames are captured`() { val sut = fixture.getSut() - assertTrue(sut.getCurrentThreads(null)!!.filter { it.stacktrace != null }.any { it.stacktrace.frames!!.count() > 0 }) + assertTrue(sut.getCurrentThreads(null)!!.filter { it.stacktrace != null }.any { it.stacktrace!!.frames!!.count() > 0 }) } @Test fun `when currentThreads is called, stack traces are snapshot`() { val sut = fixture.getSut() - assertTrue(sut.getCurrentThreads(null)!!.filter { it.stacktrace != null }.any { it.stacktrace.snapshot == true }) + assertTrue(sut.getCurrentThreads(null)!!.filter { it.stacktrace != null }.any { it.stacktrace!!.snapshot == true }) } @Test fun `when currentThreads and attachStacktrace is disabled, stack frames are not captured`() { val sut = fixture.getSut(false) - assertFalse(sut.getCurrentThreads(null)!!.filter { it.stacktrace != null }.any { it.stacktrace.frames!!.count() > 0 }) + assertFalse(sut.getCurrentThreads(null)!!.filter { it.stacktrace != null }.any { it.stacktrace!!.frames!!.count() > 0 }) } @Test @@ -86,7 +86,7 @@ class SentryThreadFactoryTest { val threads = sut.getCurrentThreads(threadList, threadIds) - assertNotNull(threads!!.firstOrNull { it.isCrashed }) + assertNotNull(threads!!.firstOrNull { it.isCrashed == true }) } @Test From 106c3e13a34f00ea0e6e9146b08701d9e9d03a47 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Wed, 28 Apr 2021 15:59:28 +0200 Subject: [PATCH 08/40] Add nullability annotations for `DebugMeta`, `SdkInfo`, `SentryException`. --- .../core/DefaultAndroidEventProcessorTest.kt | 14 +++++-- .../boot/it/SentrySpringIntegrationTest.kt | 3 +- .../spring/SentrySpringIntegrationTest.kt | 3 +- .../java/io/sentry/protocol/DebugMeta.java | 18 ++++---- .../main/java/io/sentry/protocol/SdkInfo.java | 30 ++++++------- .../io/sentry/protocol/SentryException.java | 42 ++++++++++--------- .../java/io/sentry/MainEventProcessorTest.kt | 4 +- .../io/sentry/SentryExceptionFactoryTest.kt | 11 +++-- 8 files changed, 73 insertions(+), 52 deletions(-) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt index 14e3246568..65fa6bc68b 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt @@ -106,8 +106,10 @@ class DefaultAndroidEventProcessorTest { assertNotNull(sut.process(SentryEvent(), null)) { assertNotNull(it.contexts.app) - assertEquals("test", it.debugMeta.images[0].uuid) assertNotNull(it.dist) + assertNotNull(it.debugMeta.images) { images -> + assertEquals("test", images[0].uuid) + } } } @@ -129,7 +131,9 @@ class DefaultAndroidEventProcessorTest { } assertNotNull(sut.process(event, null)) { - assertEquals("test", it.debugMeta.images[0].uuid) + assertNotNull(it.debugMeta.images) { images -> + assertEquals("test", images[0].uuid) + } } } @@ -148,8 +152,10 @@ class DefaultAndroidEventProcessorTest { } assertNotNull(sut.process(event, null)) { - assertEquals("abc", it.debugMeta.images.first().uuid) - assertEquals("test", it.debugMeta.images.last().uuid) + assertNotNull(it.debugMeta.images) { images -> + assertEquals("abc", images.first().uuid) + assertEquals("test", images.last().uuid) + } } } diff --git a/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt b/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt index 75c0e3a1c1..3daaec2405 100644 --- a/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt +++ b/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt @@ -119,7 +119,8 @@ class SentrySpringIntegrationTest { assertThat(event.exceptions).isNotEmpty val ex = event.exceptions.first() assertThat(ex.value).isEqualTo("something went wrong") - assertThat(ex.mechanism.isHandled).isFalse() + assertThat(ex.mechanism).isNotNull() + assertThat(ex.mechanism!!.isHandled).isFalse() }, anyOrNull()) } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt index e7a4331974..df1544d37b 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt @@ -127,7 +127,8 @@ class SentrySpringIntegrationTest { assertThat(event.exceptions).isNotEmpty val ex = event.exceptions.first() assertThat(ex.value).isEqualTo("something went wrong") - assertThat(ex.mechanism.isHandled).isFalse() + assertThat(ex.mechanism).isNotNull() + assertThat(ex.mechanism!!.isHandled).isFalse() }, anyOrNull()) } } diff --git a/sentry/src/main/java/io/sentry/protocol/DebugMeta.java b/sentry/src/main/java/io/sentry/protocol/DebugMeta.java index b0197b009a..599156c407 100644 --- a/sentry/src/main/java/io/sentry/protocol/DebugMeta.java +++ b/sentry/src/main/java/io/sentry/protocol/DebugMeta.java @@ -4,6 +4,8 @@ import java.util.List; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Debugging and processing meta information. @@ -18,32 +20,32 @@ */ public final class DebugMeta implements IUnknownPropertiesConsumer { /** Information about the system SDK (e.g. iOS SDK). */ - private SdkInfo sdkInfo; + private @Nullable SdkInfo sdkInfo; /** List of debug information files (debug images). */ - private List images; + private @Nullable List images; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public List getImages() { + public @Nullable List getImages() { return images; } - public void setImages(List images) { + public void setImages(final @Nullable List images) { this.images = images; } - public SdkInfo getSdkInfo() { + public @Nullable SdkInfo getSdkInfo() { return sdkInfo; } - public void setSdkInfo(SdkInfo sdkInfo) { + public void setSdkInfo(final @Nullable SdkInfo sdkInfo) { this.sdkInfo = sdkInfo; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } } diff --git a/sentry/src/main/java/io/sentry/protocol/SdkInfo.java b/sentry/src/main/java/io/sentry/protocol/SdkInfo.java index 07032c5e62..72dadf3151 100644 --- a/sentry/src/main/java/io/sentry/protocol/SdkInfo.java +++ b/sentry/src/main/java/io/sentry/protocol/SdkInfo.java @@ -3,6 +3,8 @@ import io.sentry.IUnknownPropertiesConsumer; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Holds information about the system SDK. @@ -12,52 +14,52 @@ */ public final class SdkInfo implements IUnknownPropertiesConsumer { /** The internal name of the SDK. */ - private String sdkName; + private @Nullable String sdkName; /** The major version of the SDK as integer or 0. */ - private Integer versionMajor; + private @Nullable Integer versionMajor; /** The minor version of the SDK as integer or 0. */ - private Integer versionMinor; + private @Nullable Integer versionMinor; /** The patch version of the SDK as integer or 0. */ - private Integer versionPatchlevel; + private @Nullable Integer versionPatchlevel; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getSdkName() { + public @Nullable String getSdkName() { return sdkName; } - public void setSdkName(String sdkName) { + public void setSdkName(final @Nullable String sdkName) { this.sdkName = sdkName; } - public Integer getVersionMajor() { + public @Nullable Integer getVersionMajor() { return versionMajor; } - public void setVersionMajor(Integer versionMajor) { + public void setVersionMajor(final @Nullable Integer versionMajor) { this.versionMajor = versionMajor; } - public Integer getVersionMinor() { + public @Nullable Integer getVersionMinor() { return versionMinor; } - public void setVersionMinor(Integer versionMinor) { + public void setVersionMinor(final @Nullable Integer versionMinor) { this.versionMinor = versionMinor; } - public Integer getVersionPatchlevel() { + public @Nullable Integer getVersionPatchlevel() { return versionPatchlevel; } - public void setVersionPatchlevel(Integer versionPatchlevel) { + public void setVersionPatchlevel(final @Nullable Integer versionPatchlevel) { this.versionPatchlevel = versionPatchlevel; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryException.java b/sentry/src/main/java/io/sentry/protocol/SentryException.java index 221749f623..ee71740a48 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryException.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryException.java @@ -3,6 +3,8 @@ import io.sentry.IUnknownPropertiesConsumer; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * A single exception. @@ -25,31 +27,31 @@ public final class SentryException implements IUnknownPropertiesConsumer { * *

At least one of `type` or `value` is required, otherwise the exception is discarded. */ - private String type; + private @Nullable String type; /** * Human readable display value. * *

At least one of `type` or `value` is required, otherwise the exception is discarded. */ - private String value; + private @Nullable String value; /** The optional module, or package which the exception type lives in. */ - private String module; + private @Nullable String module; /** An optional value that refers to a [thread](#typedef-Thread). */ - private Long threadId; + private @Nullable Long threadId; /** Stack trace containing frames of this exception. */ - private SentryStackTrace stacktrace; + private @Nullable SentryStackTrace stacktrace; /** Mechanism by which this exception was generated and handled. */ - private Mechanism mechanism; + private @Nullable Mechanism mechanism; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; /** * The Exception Type. * * @return the type of the exception. */ - public String getType() { + public @Nullable String getType() { return type; } @@ -58,7 +60,7 @@ public String getType() { * * @param type type of the exception. */ - public void setType(String type) { + public void setType(final @Nullable String type) { this.type = type; } @@ -67,7 +69,7 @@ public void setType(String type) { * * @return the value. */ - public String getValue() { + public @Nullable String getValue() { return value; } @@ -76,7 +78,7 @@ public String getValue() { * * @param value The exception message */ - public void setValue(String value) { + public void setValue(final @Nullable String value) { this.value = value; } @@ -85,7 +87,7 @@ public void setValue(String value) { * * @return the module. */ - public String getModule() { + public @Nullable String getModule() { return module; } @@ -94,7 +96,7 @@ public String getModule() { * * @param module the module. */ - public void setModule(String module) { + public void setModule(final @Nullable String module) { this.module = module; } @@ -103,7 +105,7 @@ public void setModule(String module) { * * @return the thread id. */ - public Long getThreadId() { + public @Nullable Long getThreadId() { return threadId; } @@ -112,7 +114,7 @@ public Long getThreadId() { * * @param threadId the thread id. */ - public void setThreadId(Long threadId) { + public void setThreadId(final @Nullable Long threadId) { this.threadId = threadId; } @@ -121,7 +123,7 @@ public void setThreadId(Long threadId) { * * @return the stacktrace. */ - public SentryStackTrace getStacktrace() { + public @Nullable SentryStackTrace getStacktrace() { return stacktrace; } @@ -130,7 +132,7 @@ public SentryStackTrace getStacktrace() { * * @param stacktrace the stacktrace of the exception. */ - public void setStacktrace(SentryStackTrace stacktrace) { + public void setStacktrace(final @Nullable SentryStackTrace stacktrace) { this.stacktrace = stacktrace; } @@ -139,7 +141,7 @@ public void setStacktrace(SentryStackTrace stacktrace) { * * @return the mechanism. */ - public Mechanism getMechanism() { + public @Nullable Mechanism getMechanism() { return mechanism; } @@ -148,13 +150,13 @@ public Mechanism getMechanism() { * * @param mechanism the mechanism. */ - public void setMechanism(Mechanism mechanism) { + public void setMechanism(final @Nullable Mechanism mechanism) { this.mechanism = mechanism; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } } diff --git a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt index 153e2fac09..9b3365658a 100644 --- a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt @@ -66,7 +66,9 @@ class MainEventProcessorTest { assertNotNull(event.threads) { assertTrue(it.first { t -> t.id == crashedThread.id }.isCrashed == true) } - assertFalse(event.exceptions.first().mechanism.isHandled) + assertNotNull(event.exceptions.first().mechanism) { + assertFalse(it.isHandled) + } } @Test diff --git a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt index 94dc52efc1..7b615bd071 100644 --- a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt @@ -8,6 +8,7 @@ import io.sentry.protocol.Mechanism import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse +import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue @@ -34,7 +35,9 @@ class SentryExceptionFactoryTest { assertEquals("Exception", sentryExceptions[0].type) assertEquals("Exception", sentryExceptions[0].value) assertEquals("java.lang", sentryExceptions[0].module) - assertTrue(sentryExceptions[0].stacktrace.frames!!.isNotEmpty()) + assertNotNull(sentryExceptions[0].stacktrace) { + assertTrue(it.frames!!.isNotEmpty()) + } } @Test @@ -74,8 +77,10 @@ class SentryExceptionFactoryTest { val throwable = ExceptionMechanismException(mechanism, error, Thread.currentThread()) val sentryExceptions = fixture.getSut().getSentryExceptions(throwable) - assertEquals("anr", sentryExceptions[0].mechanism.type) - assertFalse(sentryExceptions[0].mechanism.isHandled) + assertNotNull(sentryExceptions[0].mechanism) { + assertEquals("anr", it.type) + assertFalse(it.isHandled) + } assertNull(sentryExceptions[0].stacktrace?.snapshot) } From 0411d373ded254216f29d61b606708faa3395d7f Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Thu, 29 Apr 2021 10:41:12 +0200 Subject: [PATCH 09/40] Add nullability annotations for the rest of the protocol package. --- sentry/build.gradle.kts | 9 +- .../main/java/io/sentry/protocol/Browser.java | 2 +- .../java/io/sentry/protocol/DebugImage.java | 60 ++++----- .../main/java/io/sentry/protocol/Device.java | 2 +- .../java/io/sentry/protocol/Mechanism.java | 52 ++++---- .../main/java/io/sentry/protocol/Request.java | 2 +- .../java/io/sentry/protocol/SentryId.java | 2 +- .../io/sentry/protocol/SentryStackFrame.java | 120 +++++++++--------- .../java/io/sentry/MainEventProcessorTest.kt | 2 +- .../io/sentry/SentryExceptionFactoryTest.kt | 2 +- .../io/sentry/SentryStackTraceFactoryTest.kt | 20 +-- ...UncaughtExceptionHandlerIntegrationTest.kt | 2 +- 12 files changed, 139 insertions(+), 136 deletions(-) diff --git a/sentry/build.gradle.kts b/sentry/build.gradle.kts index a22aab8197..49f4f271ac 100644 --- a/sentry/build.gradle.kts +++ b/sentry/build.gradle.kts @@ -83,11 +83,8 @@ buildConfig { val generateBuildConfig by tasks tasks.withType() { dependsOn(generateBuildConfig) - // remove the if condition if you want to run NullAway on test code - if (!name.toLowerCase().contains("test")) { - options.errorprone { - check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) - option("NullAway:AnnotatedPackages", "io.sentry.protocol") - } + options.errorprone { + check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) + option("NullAway:AnnotatedPackages", "io.sentry.protocol") } } diff --git a/sentry/src/main/java/io/sentry/protocol/Browser.java b/sentry/src/main/java/io/sentry/protocol/Browser.java index 7c8f01a7b9..2924ce0f7b 100644 --- a/sentry/src/main/java/io/sentry/protocol/Browser.java +++ b/sentry/src/main/java/io/sentry/protocol/Browser.java @@ -43,7 +43,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/DebugImage.java b/sentry/src/main/java/io/sentry/protocol/DebugImage.java index 6c62c59b34..64fbe65ea0 100644 --- a/sentry/src/main/java/io/sentry/protocol/DebugImage.java +++ b/sentry/src/main/java/io/sentry/protocol/DebugImage.java @@ -3,6 +3,8 @@ import io.sentry.IUnknownPropertiesConsumer; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Legacy apple debug images (MachO). @@ -46,9 +48,9 @@ public final class DebugImage implements IUnknownPropertiesConsumer { * *

UUID computed from the file contents, assigned by the Java SDK. */ - private String uuid; + private @Nullable String uuid; - private String type; + private @Nullable String type; /** * Unique debug identifier of the image. * @@ -75,7 +77,7 @@ public final class DebugImage implements IUnknownPropertiesConsumer { *

- `macho`: Identifier of the dynamic library or executable. It is the value of the `LC_UUID` * load command in the Mach header, formatted as UUID. */ - private String debugId; + private @Nullable String debugId; /** * Path and name of the debug companion file. @@ -89,7 +91,7 @@ public final class DebugImage implements IUnknownPropertiesConsumer { *

- `macho`: Name or absolute path to the dSYM file containing debug information for this * image. This value might be required to retrieve debug files from certain symbol servers. */ - private String debugFile; + private @Nullable String debugFile; /** * Optional identifier of the code file. * @@ -115,7 +117,7 @@ public final class DebugImage implements IUnknownPropertiesConsumer { * load command in the Mach header, formatted as UUID. Can be empty for Mach images, as it is * equivalent to the debug identifier. */ - private String codeId; + private @Nullable String codeId; /** * Path and name of the image file (required). * @@ -125,84 +127,84 @@ public final class DebugImage implements IUnknownPropertiesConsumer { *

- `pe`: The code file should be provided to allow server-side stack walking of binary crash * reports, such as Minidumps. */ - private String codeFile; + private @Nullable String codeFile; /** * Starting memory address of the image (required). * *

Memory address, at which the image is mounted in the virtual address space of the process. * Should be a string in hex representation prefixed with `"0x"`. */ - private String imageAddr; + private @Nullable String imageAddr; /** * Size of the image in bytes (required). * *

The size of the image in virtual memory. If missing, Sentry will assume that the image spans * up to the next image, which might lead to invalid stack traces. */ - private Long imageSize; + private @Nullable Long imageSize; /** * CPU architecture target. * *

Architecture of the module. If missing, this will be backfilled by Sentry. */ - private String arch; + private @Nullable String arch; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; - public String getUuid() { + public @Nullable String getUuid() { return uuid; } - public void setUuid(String uuid) { + public void setUuid(final @Nullable String uuid) { this.uuid = uuid; } - public String getType() { + public @Nullable String getType() { return type; } - public void setType(String type) { + public void setType(final @Nullable String type) { this.type = type; } - public String getDebugId() { + public @Nullable String getDebugId() { return debugId; } - public void setDebugId(String debugId) { + public void setDebugId(final @Nullable String debugId) { this.debugId = debugId; } - public String getDebugFile() { + public @Nullable String getDebugFile() { return debugFile; } - public void setDebugFile(String debugFile) { + public void setDebugFile(final @Nullable String debugFile) { this.debugFile = debugFile; } - public String getCodeFile() { + public @Nullable String getCodeFile() { return codeFile; } - public void setCodeFile(String codeFile) { + public void setCodeFile(final @Nullable String codeFile) { this.codeFile = codeFile; } - public String getImageAddr() { + public @Nullable String getImageAddr() { return imageAddr; } - public void setImageAddr(String imageAddr) { + public void setImageAddr(final @Nullable String imageAddr) { this.imageAddr = imageAddr; } - public Long getImageSize() { + public @Nullable Long getImageSize() { return imageSize; } - public void setImageSize(Long imageSize) { + public void setImageSize(final @Nullable Long imageSize) { this.imageSize = imageSize; } @@ -215,25 +217,25 @@ public void setImageSize(long imageSize) { this.imageSize = imageSize; } - public String getArch() { + public @Nullable String getArch() { return arch; } - public void setArch(String arch) { + public void setArch(final @Nullable String arch) { this.arch = arch; } - public String getCodeId() { + public @Nullable String getCodeId() { return codeId; } - public void setCodeId(String codeId) { + public void setCodeId(final @Nullable String codeId) { this.codeId = codeId; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } } diff --git a/sentry/src/main/java/io/sentry/protocol/Device.java b/sentry/src/main/java/io/sentry/protocol/Device.java index 2e6312adbe..4af0dba444 100644 --- a/sentry/src/main/java/io/sentry/protocol/Device.java +++ b/sentry/src/main/java/io/sentry/protocol/Device.java @@ -352,7 +352,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/Mechanism.java b/sentry/src/main/java/io/sentry/protocol/Mechanism.java index 10e71d5a5e..4acffbeefb 100644 --- a/sentry/src/main/java/io/sentry/protocol/Mechanism.java +++ b/sentry/src/main/java/io/sentry/protocol/Mechanism.java @@ -3,6 +3,7 @@ import io.sentry.IUnknownPropertiesConsumer; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -14,7 +15,7 @@ * well as mechanism-specific values. */ public final class Mechanism implements IUnknownPropertiesConsumer { - private final transient Thread thread; + private final transient @Nullable Thread thread; /** * Mechanism type (required). * @@ -24,15 +25,15 @@ public final class Mechanism implements IUnknownPropertiesConsumer { *

In the Python SDK this is merely the name of the framework integration that produced the * exception, while for native it is e.g. `"minidump"` or `"applecrashreport"`. */ - private String type; + private @Nullable String type; /** * Optional human-readable description of the error mechanism. * *

May include a possible hint on how to solve this error. */ - private String description; + private @Nullable String description; /** Link to online resources describing this error. */ - private String helpLink; + private @Nullable String helpLink; /** * Flag indicating whether this exception was handled. * @@ -45,94 +46,95 @@ public final class Mechanism implements IUnknownPropertiesConsumer { *

- Exceptions captured using `capture_exception` (called from user code) are `handled=true` * as the user explicitly captured the exception (and therefore kind of handled it) */ - private Boolean handled; + private @Nullable Boolean handled; /** Operating system or runtime meta information. */ - private Map meta; + private @Nullable Map meta; /** * Arbitrary extra data that might help the user understand the error thrown by this mechanism. */ - private Map data; + private @Nullable Map data; /** * If this is set then the exception is not a real exception but some form of synthetic error for * instance from a signal handler, a hard segfault or similar where type and value are not useful * for grouping or display purposes. */ - private Boolean synthetic; + private @Nullable Boolean synthetic; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; public Mechanism() { this(null); } - public Mechanism(@Nullable Thread thread) { + public Mechanism(final @Nullable Thread thread) { this.thread = thread; } - public String getType() { + public @Nullable String getType() { return type; } - public void setType(String type) { + public void setType(final @Nullable String type) { this.type = type; } - public String getDescription() { + public @Nullable String getDescription() { return description; } - public void setDescription(String description) { + public void setDescription(final @Nullable String description) { this.description = description; } - public String getHelpLink() { + public @Nullable String getHelpLink() { return helpLink; } - public void setHelpLink(String helpLink) { + public void setHelpLink(final @Nullable String helpLink) { this.helpLink = helpLink; } - public Boolean isHandled() { + public @Nullable Boolean isHandled() { return handled; } - public void setHandled(Boolean handled) { + public void setHandled(final @Nullable Boolean handled) { this.handled = handled; } - public Map getMeta() { + public @Nullable Map getMeta() { return meta; } - public void setMeta(Map meta) { + public void setMeta(final @Nullable Map meta) { this.meta = meta; } - public Map getData() { + public @Nullable Map getData() { return data; } - public void setData(Map data) { + public void setData(final @Nullable Map data) { this.data = data; } + @Nullable Thread getThread() { return thread; } - public Boolean getSynthetic() { + public @Nullable Boolean getSynthetic() { return synthetic; } - public void setSynthetic(Boolean synthetic) { + public void setSynthetic(final @Nullable Boolean synthetic) { this.synthetic = synthetic; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } } diff --git a/sentry/src/main/java/io/sentry/protocol/Request.java b/sentry/src/main/java/io/sentry/protocol/Request.java index 1998e908a4..2505591c58 100644 --- a/sentry/src/main/java/io/sentry/protocol/Request.java +++ b/sentry/src/main/java/io/sentry/protocol/Request.java @@ -176,7 +176,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index 36d8fee8dd..f757d1d09c 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -30,7 +30,7 @@ public String toString() { } @Override - public boolean equals(Object o) { + public boolean equals(final @Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SentryId sentryId = (SentryId) o; diff --git a/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java b/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java index b6ca091eac..224f744a25 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Holds information about a single stacktrace frame. @@ -14,37 +16,37 @@ */ public final class SentryStackFrame implements IUnknownPropertiesConsumer { /** Source code leading up to `lineno`. */ - private List preContext; + private @Nullable List preContext; /** Source code of the lines after `lineno`. */ - private List postContext; + private @Nullable List postContext; /** Mapping of local variables and expression names that were available in this frame. */ - private Map vars; + private @Nullable Map vars; - private List framesOmitted; + private @Nullable List framesOmitted; /** The source file name (basename only). */ - private String filename; + private @Nullable String filename; /** * Name of the frame's function. This might include the name of a class. * *

This function name may be shortened or demangled. If not, Sentry will demangle and shorten * it for some platforms. The original function name will be stored in `raw_function`. */ - private String function; + private @Nullable String function; /** * Name of the module the frame is contained in. * *

Note that this might also include a class name if that is something the language natively * considers to be part of the stack (for instance in Java). */ - private String module; + private @Nullable String module; /** Line number within the source file, starting at 1. */ - private Integer lineno; + private @Nullable Integer lineno; /** Column number within the source file, starting at 1. */ - private Integer colno; + private @Nullable Integer colno; /** Absolute path to the source file. */ - private String absPath; + private @Nullable String absPath; /** Source code of the current line (`lineno`). */ - private String contextLine; + private @Nullable String contextLine; /** * Override whether this frame should be considered part of application code, or part of * libraries/frameworks/dependencies. @@ -52,13 +54,13 @@ public final class SentryStackFrame implements IUnknownPropertiesConsumer { *

Setting this attribute to `false` causes the frame to be hidden/collapsed by default and * mostly ignored during issue grouping. */ - private Boolean inApp; + private @Nullable Boolean inApp; @SerializedName(value = "package") - private String _package; + private @Nullable String _package; @SerializedName(value = "native") - private Boolean _native; + private @Nullable Boolean _native; /** * Which platform this frame is from. @@ -66,17 +68,17 @@ public final class SentryStackFrame implements IUnknownPropertiesConsumer { *

This can override the platform for a single frame. Otherwise, the platform of the event is * assumed. This can be used for multi-platform stack traces, such as in React Native. */ - private String platform; + private @Nullable String platform; /** (C/C++/Native) Start address of the containing code module (image). */ - private String imageAddr; + private @Nullable String imageAddr; /** * (C/C++/Native) Start address of the frame's function. * *

We use the instruction address for symbolication, but this can be used to calculate an * instruction offset automatically. */ - private String symbolAddr; + private @Nullable String symbolAddr; /** * (C/C++/Native) An optional instruction address for symbolication. * @@ -85,10 +87,10 @@ public final class SentryStackFrame implements IUnknownPropertiesConsumer { * _documentation/development/sdk-dev/event-payloads/debugmeta.md -%}), then symbolication can * take place. */ - private String instructionAddr; + private @Nullable String instructionAddr; @SuppressWarnings("unused") - private Map unknown; + private @Nullable Map unknown; /** * A raw (but potentially truncated) function value. @@ -107,163 +109,163 @@ public final class SentryStackFrame implements IUnknownPropertiesConsumer { * field will be capped at 256 characters at the moment which often means that not the entire * original value can be stored. */ - private String rawFunction; + private @Nullable String rawFunction; - public List getPreContext() { + public @Nullable List getPreContext() { return preContext; } - public void setPreContext(List preContext) { + public void setPreContext(final @Nullable List preContext) { this.preContext = preContext; } - public List getPostContext() { + public @Nullable List getPostContext() { return postContext; } - public void setPostContext(List postContext) { + public void setPostContext(final @Nullable List postContext) { this.postContext = postContext; } - public Map getVars() { + public @Nullable Map getVars() { return vars; } - public void setVars(Map vars) { + public void setVars(final @Nullable Map vars) { this.vars = vars; } - public List getFramesOmitted() { + public @Nullable List getFramesOmitted() { return framesOmitted; } - public void setFramesOmitted(List framesOmitted) { + public void setFramesOmitted(final @Nullable List framesOmitted) { this.framesOmitted = framesOmitted; } - public String getFilename() { + public @Nullable String getFilename() { return filename; } - public void setFilename(String filename) { + public void setFilename(final @Nullable String filename) { this.filename = filename; } - public String getFunction() { + public @Nullable String getFunction() { return function; } - public void setFunction(String function) { + public void setFunction(final @Nullable String function) { this.function = function; } - public String getModule() { + public @Nullable String getModule() { return module; } - public void setModule(String module) { + public void setModule(final @Nullable String module) { this.module = module; } - public Integer getLineno() { + public @Nullable Integer getLineno() { return lineno; } - public void setLineno(Integer lineno) { + public void setLineno(final @Nullable Integer lineno) { this.lineno = lineno; } - public Integer getColno() { + public @Nullable Integer getColno() { return colno; } - public void setColno(Integer colno) { + public void setColno(final @Nullable Integer colno) { this.colno = colno; } - public String getAbsPath() { + public @Nullable String getAbsPath() { return absPath; } - public void setAbsPath(String absPath) { + public void setAbsPath(final @Nullable String absPath) { this.absPath = absPath; } - public String getContextLine() { + public @Nullable String getContextLine() { return contextLine; } - public void setContextLine(String contextLine) { + public void setContextLine(final @Nullable String contextLine) { this.contextLine = contextLine; } - public Boolean isInApp() { + public @Nullable Boolean isInApp() { return inApp; } - public void setInApp(Boolean inApp) { + public void setInApp(final @Nullable Boolean inApp) { this.inApp = inApp; } - public String getPackage() { + public @Nullable String getPackage() { return _package; } - public void setPackage(String _package) { + public void setPackage(final @Nullable String _package) { this._package = _package; } - public String getPlatform() { + public @Nullable String getPlatform() { return platform; } - public void setPlatform(String platform) { + public void setPlatform(final @Nullable String platform) { this.platform = platform; } - public String getImageAddr() { + public @Nullable String getImageAddr() { return imageAddr; } - public void setImageAddr(String imageAddr) { + public void setImageAddr(final @Nullable String imageAddr) { this.imageAddr = imageAddr; } - public String getSymbolAddr() { + public @Nullable String getSymbolAddr() { return symbolAddr; } - public void setSymbolAddr(String symbolAddr) { + public void setSymbolAddr(final @Nullable String symbolAddr) { this.symbolAddr = symbolAddr; } - public String getInstructionAddr() { + public @Nullable String getInstructionAddr() { return instructionAddr; } - public void setInstructionAddr(String instructionAddr) { + public void setInstructionAddr(final @Nullable String instructionAddr) { this.instructionAddr = instructionAddr; } - public Boolean isNative() { + public @Nullable Boolean isNative() { return _native; } - public void setNative(Boolean _native) { + public void setNative(final @Nullable Boolean _native) { this._native = _native; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } - public String getRawFunction() { + public @Nullable String getRawFunction() { return rawFunction; } - public void setRawFunction(String rawFunction) { + public void setRawFunction(final @Nullable String rawFunction) { this.rawFunction = rawFunction; } } diff --git a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt index 9b3365658a..e8355833dd 100644 --- a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt @@ -67,7 +67,7 @@ class MainEventProcessorTest { assertTrue(it.first { t -> t.id == crashedThread.id }.isCrashed == true) } assertNotNull(event.exceptions.first().mechanism) { - assertFalse(it.isHandled) + assertFalse(it.isHandled!!) } } diff --git a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt index 7b615bd071..c17f082d69 100644 --- a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt @@ -79,7 +79,7 @@ class SentryExceptionFactoryTest { val sentryExceptions = fixture.getSut().getSentryExceptions(throwable) assertNotNull(sentryExceptions[0].mechanism) { assertEquals("anr", it.type) - assertFalse(it.isHandled) + assertFalse(it.isHandled!!) } assertNull(sentryExceptions[0].stacktrace?.snapshot) } diff --git a/sentry/src/test/java/io/sentry/SentryStackTraceFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryStackTraceFactoryTest.kt index 78cfd054a0..c658eee88a 100644 --- a/sentry/src/test/java/io/sentry/SentryStackTraceFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryStackTraceFactoryTest.kt @@ -62,7 +62,7 @@ class SentryStackTraceFactoryTest { val sentryStackTraceFactory = SentryStackTraceFactory(listOf("io.mysentry"), null) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) - assertFalse(sentryElements!!.first().isInApp) + assertFalse(sentryElements!!.first().isInApp!!) } @Test @@ -72,7 +72,7 @@ class SentryStackTraceFactoryTest { val sentryStackTraceFactory = SentryStackTraceFactory(listOf("io.mysentry"), null) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) - assertFalse(sentryElements!!.first().isInApp) + assertFalse(sentryElements!!.first().isInApp!!) } @Test @@ -82,7 +82,7 @@ class SentryStackTraceFactoryTest { val sentryStackTraceFactory = SentryStackTraceFactory(null, null) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) - assertFalse(sentryElements!!.first().isInApp) + assertFalse(sentryElements!!.first().isInApp!!) } //endregion @@ -94,7 +94,7 @@ class SentryStackTraceFactoryTest { val sentryStackTraceFactory = SentryStackTraceFactory(null, listOf("io.mysentry")) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) - assertTrue(sentryElements!!.first().isInApp) + assertTrue(sentryElements!!.first().isInApp!!) } @Test @@ -104,7 +104,7 @@ class SentryStackTraceFactoryTest { val sentryStackTraceFactory = SentryStackTraceFactory(null, listOf("io.mysentry")) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) - assertFalse(sentryElements!!.first().isInApp) + assertFalse(sentryElements!!.first().isInApp!!) } @Test @@ -114,7 +114,7 @@ class SentryStackTraceFactoryTest { val sentryStackTraceFactory = SentryStackTraceFactory(null, null) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) - assertFalse(sentryElements!!.first().isInApp) + assertFalse(sentryElements!!.first().isInApp!!) } //endregion @@ -125,7 +125,7 @@ class SentryStackTraceFactoryTest { val sentryStackTraceFactory = SentryStackTraceFactory(listOf("io.mysentry"), listOf("io.mysentry")) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) - assertTrue(sentryElements!!.first().isInApp) + assertTrue(sentryElements!!.first().isInApp!!) } @Test @@ -150,7 +150,7 @@ class SentryStackTraceFactoryTest { stacktrace = stacktrace.plusElement(sentryElement) assertNull(sut.getStackFrames(stacktrace)!!.find { - it.module.startsWith("io.sentry") + it.module != null && it.module!!.startsWith("io.sentry") }) } @@ -161,7 +161,7 @@ class SentryStackTraceFactoryTest { stacktrace = stacktrace.plusElement(sentryElement) assertNotNull(sut.getStackFrames(stacktrace)!!.find { - it.module.startsWith("io.sentry") + it.module != null && it.module!!.startsWith("io.sentry") }) } @@ -172,7 +172,7 @@ class SentryStackTraceFactoryTest { stacktrace = stacktrace.plusElement(sentryElement) assertNotNull(sut.getStackFrames(stacktrace)!!.find { - it.module.startsWith("io.sentry") + it.module != null && it.module!!.startsWith("io.sentry") }) } diff --git a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt index 218a62b87d..2a822e1429 100644 --- a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt @@ -77,7 +77,7 @@ class UncaughtExceptionHandlerIntegrationTest { val e = (invocation.arguments[1] as ExceptionMechanismException) assertNotNull(e) assertNotNull(e.exceptionMechanism) - assertTrue(e.exceptionMechanism.isHandled) + assertTrue(e.exceptionMechanism.isHandled!!) SentryId.EMPTY_ID } val options = SentryOptions().noFlushTimeout() From 2cf4f90f7b8a20e712f30afdec7a2a16abc56b66 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Thu, 29 Apr 2021 22:32:04 +0200 Subject: [PATCH 10/40] Add nullability annotations to `transport` package. --- .../sentry/transport/apache/ApacheHttpClientTransport.java | 3 ++- .../main/java/io/sentry/transport/AsyncHttpTransport.java | 2 +- .../src/main/java/io/sentry/transport/HttpConnection.java | 1 + sentry/src/main/java/io/sentry/transport/ITransport.java | 7 +++++-- .../src/main/java/io/sentry/transport/NoOpTransport.java | 6 ++++-- .../main/java/io/sentry/transport/ProxyAuthenticator.java | 7 ++++--- .../src/main/java/io/sentry/transport/StdoutTransport.java | 3 ++- 7 files changed, 19 insertions(+), 10 deletions(-) diff --git a/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java b/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java index 7d734a6a4d..01c6b38c11 100644 --- a/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java +++ b/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java @@ -25,6 +25,7 @@ import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.TimeValue; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * {@link ITransport} implementation that executes request asynchronously in a non-blocking manner @@ -62,7 +63,7 @@ public ApacheHttpClientTransport( @Override @SuppressWarnings("FutureReturnValueIgnored") - public void send(SentryEnvelope envelope, Object hint) throws IOException { + public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) throws IOException { if (isSchedulingAllowed()) { final SentryEnvelope filteredEnvelope = rateLimiter.filter(envelope, hint); diff --git a/sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java b/sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java index ac2a953334..7c85b8869b 100644 --- a/sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java +++ b/sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java @@ -63,7 +63,7 @@ public AsyncHttpTransport( @Override @SuppressWarnings("FutureReturnValueIgnored") - public void send(SentryEnvelope envelope, Object hint) throws IOException { + public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) throws IOException { // For now no caching on envelopes IEnvelopeCache currentEnvelopeCache = envelopeCache; boolean cached = false; diff --git a/sentry/src/main/java/io/sentry/transport/HttpConnection.java b/sentry/src/main/java/io/sentry/transport/HttpConnection.java index d7f1a4455a..802e161c54 100644 --- a/sentry/src/main/java/io/sentry/transport/HttpConnection.java +++ b/sentry/src/main/java/io/sentry/transport/HttpConnection.java @@ -274,6 +274,7 @@ private boolean isSuccessfulResponseCode(final int responseCode) { } @TestOnly + @Nullable Proxy getProxy() { return proxy; } diff --git a/sentry/src/main/java/io/sentry/transport/ITransport.java b/sentry/src/main/java/io/sentry/transport/ITransport.java index 0fa87c5004..489ad0042b 100644 --- a/sentry/src/main/java/io/sentry/transport/ITransport.java +++ b/sentry/src/main/java/io/sentry/transport/ITransport.java @@ -1,14 +1,17 @@ package io.sentry.transport; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import io.sentry.SentryEnvelope; import java.io.Closeable; import java.io.IOException; /** A transport is in charge of sending the event to the Sentry server. */ public interface ITransport extends Closeable { - void send(SentryEnvelope envelope, Object hint) throws IOException; + void send(@NotNull SentryEnvelope envelope, @Nullable Object hint) throws IOException; - default void send(SentryEnvelope envelope) throws IOException { + default void send(@NotNull SentryEnvelope envelope) throws IOException { send(envelope, null); } diff --git a/sentry/src/main/java/io/sentry/transport/NoOpTransport.java b/sentry/src/main/java/io/sentry/transport/NoOpTransport.java index a70371f654..0a8306cca9 100644 --- a/sentry/src/main/java/io/sentry/transport/NoOpTransport.java +++ b/sentry/src/main/java/io/sentry/transport/NoOpTransport.java @@ -3,20 +3,22 @@ import io.sentry.SentryEnvelope; import java.io.IOException; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class NoOpTransport implements ITransport { private static final NoOpTransport instance = new NoOpTransport(); - public static NoOpTransport getInstance() { + public static @NotNull NoOpTransport getInstance() { return instance; } private NoOpTransport() {} @Override - public void send(SentryEnvelope envelope, Object hint) throws IOException {} + public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) throws IOException {} @Override public void flush(long timeoutMillis) {} diff --git a/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java b/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java index 53d54e128e..f934729be9 100644 --- a/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java +++ b/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java @@ -4,6 +4,7 @@ import java.net.Authenticator; import java.net.PasswordAuthentication; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; final class ProxyAuthenticator extends Authenticator { private final @NotNull String user; @@ -21,18 +22,18 @@ final class ProxyAuthenticator extends Authenticator { } @Override - protected PasswordAuthentication getPasswordAuthentication() { + protected @Nullable PasswordAuthentication getPasswordAuthentication() { if (getRequestorType() == RequestorType.PROXY) { return new PasswordAuthentication(user, password.toCharArray()); } return null; } - String getUser() { + @NotNull String getUser() { return user; } - String getPassword() { + @NotNull String getPassword() { return password; } } diff --git a/sentry/src/main/java/io/sentry/transport/StdoutTransport.java b/sentry/src/main/java/io/sentry/transport/StdoutTransport.java index de2a92f956..48084b03e8 100644 --- a/sentry/src/main/java/io/sentry/transport/StdoutTransport.java +++ b/sentry/src/main/java/io/sentry/transport/StdoutTransport.java @@ -5,6 +5,7 @@ import io.sentry.util.Objects; import java.io.IOException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public final class StdoutTransport implements ITransport { @@ -15,7 +16,7 @@ public StdoutTransport(final @NotNull ISerializer serializer) { } @Override - public void send(final @NotNull SentryEnvelope envelope, Object hint) throws IOException { + public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) throws IOException { Objects.requireNonNull(envelope, "SentryEnvelope is required"); try { From 3c732212864032e2ea5a0afe4a3a0654618a1f2d Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Thu, 29 Apr 2021 22:32:12 +0200 Subject: [PATCH 11/40] Add nullability annotations to `util` package. --- sentry/src/main/java/io/sentry/util/CollectionUtils.java | 2 +- sentry/src/main/java/io/sentry/util/Objects.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/util/CollectionUtils.java b/sentry/src/main/java/io/sentry/util/CollectionUtils.java index d749c4a0c4..5f118fc635 100644 --- a/sentry/src/main/java/io/sentry/util/CollectionUtils.java +++ b/sentry/src/main/java/io/sentry/util/CollectionUtils.java @@ -19,7 +19,7 @@ private CollectionUtils() {} * @param data the Iterable * @return iterator size */ - public static int size(Iterable data) { + public static int size(final @NotNull Iterable data) { if (data instanceof Collection) { return ((Collection) data).size(); } diff --git a/sentry/src/main/java/io/sentry/util/Objects.java b/sentry/src/main/java/io/sentry/util/Objects.java index f39fa2c588..1ab070157b 100644 --- a/sentry/src/main/java/io/sentry/util/Objects.java +++ b/sentry/src/main/java/io/sentry/util/Objects.java @@ -1,12 +1,14 @@ package io.sentry.util; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class Objects { private Objects() {} - public static T requireNonNull(T obj, String message) { + public static T requireNonNull(final @Nullable T obj, final @NotNull String message) { if (obj == null) throw new IllegalArgumentException(message); return obj; } From 5bc9b98676858b818b8ba5f5a0206b99c9c41120 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 30 Apr 2021 09:39:40 +0200 Subject: [PATCH 12/40] Add nullability annotations to `transport` package. --- .../main/java/io/sentry/transport/AsyncHttpTransport.java | 3 ++- sentry/src/main/java/io/sentry/transport/ITransport.java | 5 ++--- sentry/src/main/java/io/sentry/transport/NoOpTransport.java | 3 ++- .../main/java/io/sentry/transport/ProxyAuthenticator.java | 6 ++++-- .../src/main/java/io/sentry/transport/StdoutTransport.java | 3 ++- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java b/sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java index 7c85b8869b..b8be111b73 100644 --- a/sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java +++ b/sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java @@ -63,7 +63,8 @@ public AsyncHttpTransport( @Override @SuppressWarnings("FutureReturnValueIgnored") - public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) throws IOException { + public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) + throws IOException { // For now no caching on envelopes IEnvelopeCache currentEnvelopeCache = envelopeCache; boolean cached = false; diff --git a/sentry/src/main/java/io/sentry/transport/ITransport.java b/sentry/src/main/java/io/sentry/transport/ITransport.java index 489ad0042b..1a677fe233 100644 --- a/sentry/src/main/java/io/sentry/transport/ITransport.java +++ b/sentry/src/main/java/io/sentry/transport/ITransport.java @@ -1,11 +1,10 @@ package io.sentry.transport; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import io.sentry.SentryEnvelope; import java.io.Closeable; import java.io.IOException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** A transport is in charge of sending the event to the Sentry server. */ public interface ITransport extends Closeable { diff --git a/sentry/src/main/java/io/sentry/transport/NoOpTransport.java b/sentry/src/main/java/io/sentry/transport/NoOpTransport.java index 0a8306cca9..c5721d99f7 100644 --- a/sentry/src/main/java/io/sentry/transport/NoOpTransport.java +++ b/sentry/src/main/java/io/sentry/transport/NoOpTransport.java @@ -18,7 +18,8 @@ public final class NoOpTransport implements ITransport { private NoOpTransport() {} @Override - public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) throws IOException {} + public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) + throws IOException {} @Override public void flush(long timeoutMillis) {} diff --git a/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java b/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java index f934729be9..7aad9967f1 100644 --- a/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java +++ b/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java @@ -29,11 +29,13 @@ final class ProxyAuthenticator extends Authenticator { return null; } - @NotNull String getUser() { + @NotNull + String getUser() { return user; } - @NotNull String getPassword() { + @NotNull + String getPassword() { return password; } } diff --git a/sentry/src/main/java/io/sentry/transport/StdoutTransport.java b/sentry/src/main/java/io/sentry/transport/StdoutTransport.java index 48084b03e8..48fb983686 100644 --- a/sentry/src/main/java/io/sentry/transport/StdoutTransport.java +++ b/sentry/src/main/java/io/sentry/transport/StdoutTransport.java @@ -16,7 +16,8 @@ public StdoutTransport(final @NotNull ISerializer serializer) { } @Override - public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) throws IOException { + public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) + throws IOException { Objects.requireNonNull(envelope, "SentryEnvelope is required"); try { From 37a1274932a91e3b594d6496f3a0ed6ffda292ae Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 30 Apr 2021 09:39:49 +0200 Subject: [PATCH 13/40] Add nullability annotations to `SentrySpan` --- sentry/src/main/java/io/sentry/protocol/SentrySpan.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/protocol/SentrySpan.java b/sentry/src/main/java/io/sentry/protocol/SentrySpan.java index 80b6ead4d0..07dfed2e37 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentrySpan.java +++ b/sentry/src/main/java/io/sentry/protocol/SentrySpan.java @@ -7,6 +7,7 @@ import io.sentry.util.Objects; import java.util.Date; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,7 +32,8 @@ public SentrySpan(final @NotNull Span span) { this.parentSpanId = span.getParentSpanId(); this.traceId = span.getTraceId(); this.status = span.getStatus(); - this.tags = CollectionUtils.shallowCopy(span.getTags()); + final Map tagsCopy = CollectionUtils.shallowCopy(span.getTags()); + this.tags = tagsCopy != null ? tagsCopy : new ConcurrentHashMap<>(); this.timestamp = span.getTimestamp(); this.startTimestamp = span.getStartTimestamp(); } From f19c1c5a2ad397ea68f1e97f2726021a6ba945e2 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 30 Apr 2021 09:40:14 +0200 Subject: [PATCH 14/40] Add nullability annotations. --- .../io/sentry/android/core/AndroidLogger.java | 5 +- .../apache/ApacheHttpClientTransport.java | 3 +- .../src/main/java/io/sentry/Breadcrumb.java | 9 ++- .../java/io/sentry/CustomSamplingContext.java | 4 +- sentry/src/main/java/io/sentry/Hub.java | 8 +- .../src/main/java/io/sentry/HubAdapter.java | 48 +++++------ sentry/src/main/java/io/sentry/IHub.java | 81 ++++++++++--------- sentry/src/main/java/io/sentry/ILogger.java | 4 +- .../main/java/io/sentry/ISentryClient.java | 7 +- sentry/src/main/java/io/sentry/NoOpHub.java | 37 ++++----- .../main/java/io/sentry/NoOpSentryClient.java | 4 +- .../src/main/java/io/sentry/SentryClient.java | 2 +- .../main/java/io/sentry/SentryOptions.java | 6 +- .../src/main/java/io/sentry/SpanContext.java | 5 +- sentry/src/test/java/io/sentry/HubTest.kt | 79 ------------------ sentry/src/test/java/io/sentry/NoOpHubTest.kt | 23 ++---- .../java/io/sentry/NoOpSentryClientTest.kt | 3 +- 17 files changed, 134 insertions(+), 194 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java index a80a603a60..a81f73cb4d 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java @@ -11,7 +11,10 @@ final class AndroidLogger implements ILogger { @SuppressWarnings("AnnotateFormatMethod") @Override - public void log(SentryLevel level, String message, Object... args) { + public void log( + final @Nullable SentryLevel level, + final @Nullable String message, + final @Nullable Object... args) { Log.println(toLogcatLevel(level), tag, String.format(message, args)); } diff --git a/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java b/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java index 01c6b38c11..cf332bab1d 100644 --- a/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java +++ b/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java @@ -63,7 +63,8 @@ public ApacheHttpClientTransport( @Override @SuppressWarnings("FutureReturnValueIgnored") - public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) throws IOException { + public void send(final @NotNull SentryEnvelope envelope, final @Nullable Object hint) + throws IOException { if (isSchedulingAllowed()) { final SentryEnvelope filteredEnvelope = rateLimiter.filter(envelope, hint); diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index 5da54aa209..ecf4202c66 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -14,7 +14,7 @@ public final class Breadcrumb implements Cloneable, IUnknownPropertiesConsumer { /** A timestamp representing when the breadcrumb occurred. */ - private final @Nullable Date timestamp; + private final @NotNull Date timestamp; /** If a message is provided, its rendered as text and the whitespace is preserved. */ private @Nullable String message; @@ -39,7 +39,7 @@ public final class Breadcrumb implements Cloneable, IUnknownPropertiesConsumer { * * @param timestamp the timestamp */ - public Breadcrumb(final @Nullable Date timestamp) { + public Breadcrumb(final @NotNull Date timestamp) { this.timestamp = timestamp; } @@ -247,7 +247,10 @@ Map getUnknown() { public @NotNull Breadcrumb clone() throws CloneNotSupportedException { final Breadcrumb clone = (Breadcrumb) super.clone(); - clone.data = CollectionUtils.shallowCopy(data); + final Map dataCopy = CollectionUtils.shallowCopy(this.data); + if (dataCopy != null) { + clone.data = dataCopy; + } clone.unknown = CollectionUtils.shallowCopy(unknown); final SentryLevel levelRef = level; diff --git a/sentry/src/main/java/io/sentry/CustomSamplingContext.java b/sentry/src/main/java/io/sentry/CustomSamplingContext.java index e26b698936..fccd0603f0 100644 --- a/sentry/src/main/java/io/sentry/CustomSamplingContext.java +++ b/sentry/src/main/java/io/sentry/CustomSamplingContext.java @@ -18,12 +18,12 @@ public void set(final @NotNull String key, final @Nullable Object value) { this.data.put(key, value); } - public Object get(final @NotNull String key) { + public @Nullable Object get(final @NotNull String key) { Objects.requireNonNull(key, "key is required"); return this.data.get(key); } - public Map getData() { + public @NotNull Map getData() { return data; } } diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index a0cfa4b4b0..eab821fc7c 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -122,7 +122,7 @@ public boolean isEnabled() { @ApiStatus.Internal @Override - public SentryId captureEnvelope( + public @NotNull SentryId captureEnvelope( final @NotNull SentryEnvelope envelope, final @Nullable Object hint) { Objects.requireNonNull(envelope, "SentryEnvelope is required."); @@ -177,10 +177,10 @@ private void assignTraceContext(final @NotNull SentryEvent event) { if (event.getThrowable() != null) { final Pair pair = throwableToSpan.get(event.getThrowable()); if (pair != null) { - if (event.getContexts().getTrace() == null) { + if (event.getContexts().getTrace() == null && pair.getFirst() != null) { event.getContexts().setTrace(pair.getFirst().getSpanContext()); } - if (event.getTransaction() == null) { + if (event.getTransaction() == null && pair.getSecond() != null) { event.setTransaction(pair.getSecond()); } } @@ -188,7 +188,7 @@ private void assignTraceContext(final @NotNull SentryEvent event) { } @Override - public void captureUserFeedback(UserFeedback userFeedback) { + public void captureUserFeedback(@NotNull UserFeedback userFeedback) { if (!isEnabled()) { options .getLogger() diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index d8e617f6fb..a6e03ae7ef 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -24,28 +24,29 @@ public boolean isEnabled() { } @Override - public SentryId captureEvent(SentryEvent event, @Nullable Object hint) { + public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Object hint) { return Sentry.captureEvent(event, hint); } @Override - public SentryId captureMessage(String message, SentryLevel level) { + public @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { return Sentry.captureMessage(message, level); } @ApiStatus.Internal @Override - public SentryId captureEnvelope(SentryEnvelope envelope, @Nullable Object hint) { + public @NotNull SentryId captureEnvelope( + @NotNull SentryEnvelope envelope, @Nullable Object hint) { return Sentry.getCurrentHub().captureEnvelope(envelope, hint); } @Override - public SentryId captureException(Throwable throwable, @Nullable Object hint) { + public @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Object hint) { return Sentry.captureException(throwable, hint); } @Override - public void captureUserFeedback(UserFeedback userFeedback) { + public void captureUserFeedback(@NotNull UserFeedback userFeedback) { Sentry.captureUserFeedback(userFeedback); } @@ -65,27 +66,27 @@ public void close() { } @Override - public void addBreadcrumb(Breadcrumb breadcrumb, @Nullable Object hint) { + public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Object hint) { Sentry.addBreadcrumb(breadcrumb, hint); } @Override - public void setLevel(SentryLevel level) { + public void setLevel(@Nullable SentryLevel level) { Sentry.setLevel(level); } @Override - public void setTransaction(String transaction) { + public void setTransaction(@Nullable String transaction) { Sentry.setTransaction(transaction); } @Override - public void setUser(User user) { + public void setUser(@Nullable User user) { Sentry.setUser(user); } @Override - public void setFingerprint(List fingerprint) { + public void setFingerprint(@NotNull List fingerprint) { Sentry.setFingerprint(fingerprint); } @@ -95,27 +96,27 @@ public void clearBreadcrumbs() { } @Override - public void setTag(String key, String value) { + public void setTag(@NotNull String key, @NotNull String value) { Sentry.setTag(key, value); } @Override - public void removeTag(String key) { + public void removeTag(@NotNull String key) { Sentry.removeTag(key); } @Override - public void setExtra(String key, String value) { + public void setExtra(@NotNull String key, @NotNull String value) { Sentry.setExtra(key, value); } @Override - public void removeExtra(String key) { + public void removeExtra(@NotNull String key) { Sentry.removeExtra(key); } @Override - public SentryId getLastEventId() { + public @NotNull SentryId getLastEventId() { return Sentry.getLastEventId(); } @@ -130,17 +131,17 @@ public void popScope() { } @Override - public void withScope(ScopeCallback callback) { + public void withScope(@NotNull ScopeCallback callback) { Sentry.withScope(callback); } @Override - public void configureScope(ScopeCallback callback) { + public void configureScope(@NotNull ScopeCallback callback) { Sentry.configureScope(callback); } @Override - public void bindClient(ISentryClient client) { + public void bindClient(@NotNull ISentryClient client) { Sentry.bindClient(client); } @@ -150,24 +151,25 @@ public void flush(long timeoutMillis) { } @Override - public IHub clone() { + public @NotNull IHub clone() { return Sentry.getCurrentHub().clone(); } @Override - public @NotNull SentryId captureTransaction(SentryTransaction transaction, Object hint) { + public @NotNull SentryId captureTransaction( + @NotNull SentryTransaction transaction, @Nullable Object hint) { return Sentry.getCurrentHub().captureTransaction(transaction, hint); } @Override - public @NotNull ITransaction startTransaction(TransactionContext transactionContexts) { + public @NotNull ITransaction startTransaction(@NotNull TransactionContext transactionContexts) { return Sentry.startTransaction(transactionContexts); } @Override public @NotNull ITransaction startTransaction( - TransactionContext transactionContexts, - CustomSamplingContext customSamplingContext, + @NotNull TransactionContext transactionContexts, + @Nullable CustomSamplingContext customSamplingContext, boolean bindToScope) { return Sentry.startTransaction(transactionContexts, customSamplingContext, bindToScope); } diff --git a/sentry/src/main/java/io/sentry/IHub.java b/sentry/src/main/java/io/sentry/IHub.java index 4c9f3b5a1f..5f3ca8ccb0 100644 --- a/sentry/src/main/java/io/sentry/IHub.java +++ b/sentry/src/main/java/io/sentry/IHub.java @@ -25,7 +25,8 @@ public interface IHub { * @param hint SDK specific but provides high level information about the origin of the event * @return The Id (SentryId object) of the event */ - SentryId captureEvent(SentryEvent event, @Nullable Object hint); + @NotNull + SentryId captureEvent(@NotNull SentryEvent event, @Nullable Object hint); /** * Captures the event. @@ -33,7 +34,7 @@ public interface IHub { * @param event the event * @return The Id (SentryId object) of the event */ - default SentryId captureEvent(SentryEvent event) { + default @NotNull SentryId captureEvent(@NotNull SentryEvent event) { return captureEvent(event, null); } @@ -43,7 +44,7 @@ default SentryId captureEvent(SentryEvent event) { * @param message The message to send. * @return The Id (SentryId object) of the event */ - default SentryId captureMessage(String message) { + default @NotNull SentryId captureMessage(@NotNull String message) { return captureMessage(message, SentryLevel.INFO); } @@ -54,7 +55,8 @@ default SentryId captureMessage(String message) { * @param level The message level. * @return The Id (SentryId object) of the event */ - SentryId captureMessage(String message, SentryLevel level); + @NotNull + SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level); /** * Captures an envelope. @@ -63,7 +65,8 @@ default SentryId captureMessage(String message) { * @param hint SDK specific but provides high level information about the origin of the event * @return The Id (SentryId object) of the event */ - SentryId captureEnvelope(SentryEnvelope envelope, @Nullable Object hint); + @NotNull + SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Object hint); /** * Captures an envelope. @@ -71,7 +74,7 @@ default SentryId captureMessage(String message) { * @param envelope the SentryEnvelope to send. * @return The Id (SentryId object) of the event */ - default SentryId captureEnvelope(SentryEnvelope envelope) { + default @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope) { return captureEnvelope(envelope, null); } @@ -82,7 +85,8 @@ default SentryId captureEnvelope(SentryEnvelope envelope) { * @param hint SDK specific but provides high level information about the origin of the event * @return The Id (SentryId object) of the event */ - SentryId captureException(Throwable throwable, @Nullable Object hint); + @NotNull + SentryId captureException(@NotNull Throwable throwable, @Nullable Object hint); /** * Captures the exception. @@ -90,7 +94,7 @@ default SentryId captureEnvelope(SentryEnvelope envelope) { * @param throwable The exception. * @return The Id (SentryId object) of the event */ - default SentryId captureException(Throwable throwable) { + default @NotNull SentryId captureException(@NotNull Throwable throwable) { return captureException(throwable, null); } @@ -99,7 +103,7 @@ default SentryId captureException(Throwable throwable) { * * @param userFeedback The user feedback to send to Sentry. */ - void captureUserFeedback(UserFeedback userFeedback); + void captureUserFeedback(@NotNull UserFeedback userFeedback); /** Starts a new session. If there's a running session, it ends it before starting the new one. */ void startSession(); @@ -116,14 +120,14 @@ default SentryId captureException(Throwable throwable) { * @param breadcrumb the breadcrumb * @param hint SDK specific but provides high level information about the origin of the event */ - void addBreadcrumb(Breadcrumb breadcrumb, @Nullable Object hint); + void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Object hint); /** * Adds a breadcrumb to the current Scope * * @param breadcrumb the breadcrumb */ - default void addBreadcrumb(Breadcrumb breadcrumb) { + default void addBreadcrumb(@NotNull Breadcrumb breadcrumb) { addBreadcrumb(breadcrumb, null); } @@ -154,28 +158,28 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * * @param level the Sentry level */ - void setLevel(SentryLevel level); + void setLevel(@Nullable SentryLevel level); /** * Sets the name of the current transaction to the current Scope. * * @param transaction the transaction */ - void setTransaction(String transaction); + void setTransaction(@Nullable String transaction); /** * Shallow merges user configuration (email, username, etc) to the current Scope. * * @param user the user */ - void setUser(User user); + void setUser(@Nullable User user); /** * Sets the fingerprint to group specific events together to the current Scope. * * @param fingerprint the fingerprints */ - void setFingerprint(List fingerprint); + void setFingerprint(@NotNull List fingerprint); /** Deletes current breadcrumbs from the current scope. */ void clearBreadcrumbs(); @@ -186,14 +190,14 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * @param key the key * @param value the value */ - void setTag(String key, String value); + void setTag(@NotNull String key, @NotNull String value); /** * Removes the tag to a string value to the current Scope * * @param key the key */ - void removeTag(String key); + void removeTag(@NotNull String key); /** * Sets the extra key to an arbitrary value to the current Scope, overwriting a potential previous @@ -202,20 +206,21 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * @param key the key * @param value the value */ - void setExtra(String key, String value); + void setExtra(@NotNull String key, @NotNull String value); /** * Removes the extra key to an arbitrary value to the current Scope * * @param key the key */ - void removeExtra(String key); + void removeExtra(@NotNull String key); /** * Last event id recorded in the current scope * * @return last SentryId */ + @Nullable SentryId getLastEventId(); /** Pushes a new scope while inheriting the current scope's data. */ @@ -229,21 +234,21 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * * @param callback the callback */ - void withScope(ScopeCallback callback); + void withScope(@NotNull ScopeCallback callback); /** * Configures the scope through the callback. * * @param callback The configure scope callback. */ - void configureScope(ScopeCallback callback); + void configureScope(@NotNull ScopeCallback callback); /** * Binds a different client to the hub * * @param client the client. */ - void bindClient(ISentryClient client); + void bindClient(@NotNull ISentryClient client); /** * Flushes events queued up, but keeps the Hub enabled. Not implemented yet. @@ -257,6 +262,7 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * * @return the cloned Hub */ + @NotNull IHub clone(); /** @@ -267,7 +273,8 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * @return transaction's id */ @ApiStatus.Internal - SentryId captureTransaction(SentryTransaction transaction, Object hint); + @NotNull + SentryId captureTransaction(@NotNull SentryTransaction transaction, @Nullable Object hint); /** * Captures the transaction and enqueues it for sending to Sentry server. @@ -276,7 +283,7 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * @return transaction's id */ @ApiStatus.Internal - default SentryId captureTransaction(SentryTransaction transaction) { + default @NotNull SentryId captureTransaction(@NotNull SentryTransaction transaction) { return captureTransaction(transaction, null); } @@ -286,7 +293,7 @@ default SentryId captureTransaction(SentryTransaction transaction) { * @param transactionContexts the transaction contexts * @return created transaction */ - default ITransaction startTransaction(TransactionContext transactionContexts) { + default @NotNull ITransaction startTransaction(@NotNull TransactionContext transactionContexts) { return startTransaction(transactionContexts, false); } @@ -297,8 +304,8 @@ default ITransaction startTransaction(TransactionContext transactionContexts) { * @param bindToScope if transaction should be bound to scope * @return created transaction */ - default ITransaction startTransaction( - TransactionContext transactionContexts, boolean bindToScope) { + default @NotNull ITransaction startTransaction( + @NotNull TransactionContext transactionContexts, boolean bindToScope) { return startTransaction(transactionContexts, null, bindToScope); } @@ -312,7 +319,9 @@ default ITransaction startTransaction( * @return created transaction. */ default @NotNull ITransaction startTransaction( - String name, String operation, CustomSamplingContext customSamplingContext) { + @NotNull String name, + @NotNull String operation, + @Nullable CustomSamplingContext customSamplingContext) { return startTransaction(name, operation, customSamplingContext, false); } @@ -327,9 +336,9 @@ default ITransaction startTransaction( * @return created transaction. */ default @NotNull ITransaction startTransaction( - String name, - String operation, - CustomSamplingContext customSamplingContext, + @NotNull String name, + @NotNull String operation, + @Nullable CustomSamplingContext customSamplingContext, boolean bindToScope) { return startTransaction( new TransactionContext(name, operation), customSamplingContext, bindToScope); @@ -343,9 +352,9 @@ default ITransaction startTransaction( * @param customSamplingContext the sampling context * @return created transaction. */ - @NotNull - default ITransaction startTransaction( - TransactionContext transactionContexts, CustomSamplingContext customSamplingContext) { + default @NotNull ITransaction startTransaction( + @NotNull TransactionContext transactionContexts, + @Nullable CustomSamplingContext customSamplingContext) { return startTransaction(transactionContexts, customSamplingContext, false); } @@ -360,8 +369,8 @@ default ITransaction startTransaction( */ @NotNull ITransaction startTransaction( - TransactionContext transactionContexts, - CustomSamplingContext customSamplingContext, + @NotNull TransactionContext transactionContexts, + @Nullable CustomSamplingContext customSamplingContext, boolean bindToScope); /** diff --git a/sentry/src/main/java/io/sentry/ILogger.java b/sentry/src/main/java/io/sentry/ILogger.java index b65cebd6e0..e5a009854b 100644 --- a/sentry/src/main/java/io/sentry/ILogger.java +++ b/sentry/src/main/java/io/sentry/ILogger.java @@ -12,7 +12,7 @@ public interface ILogger { * @param message The message. * @param args The optional arguments to format the message. */ - void log(SentryLevel level, String message, Object... args); + void log(@Nullable SentryLevel level, @Nullable String message, @Nullable Object... args); /** * Logs a message with the specified level, message and optional arguments. @@ -21,7 +21,7 @@ public interface ILogger { * @param message The message. * @param throwable The throwable to log. */ - void log(SentryLevel level, String message, Throwable throwable); + void log(@Nullable SentryLevel level, @Nullable String message, @Nullable Throwable throwable); /** * Logs a message with the specified level, throwable, message and optional arguments. diff --git a/sentry/src/main/java/io/sentry/ISentryClient.java b/sentry/src/main/java/io/sentry/ISentryClient.java index b92ef21b74..9337dbd0ce 100644 --- a/sentry/src/main/java/io/sentry/ISentryClient.java +++ b/sentry/src/main/java/io/sentry/ISentryClient.java @@ -3,6 +3,7 @@ import io.sentry.protocol.Message; import io.sentry.protocol.SentryId; import io.sentry.protocol.SentryTransaction; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** Sentry Client interface */ @@ -195,7 +196,9 @@ default SentryId captureEnvelope(SentryEnvelope envelope) { * @param hint SDK specific but provides high level information about the origin of the event * @return The Id (SentryId object) of the event */ - SentryId captureTransaction(SentryTransaction transaction, Scope scope, Object hint); + @NotNull + SentryId captureTransaction( + @NotNull SentryTransaction transaction, @NotNull Scope scope, @Nullable Object hint); /** * Captures a transaction without scope nor hint. @@ -203,7 +206,7 @@ default SentryId captureEnvelope(SentryEnvelope envelope) { * @param transaction the {@link ITransaction} to send * @return The Id (SentryId object) of the event */ - default SentryId captureTransaction(SentryTransaction transaction) { + default @NotNull SentryId captureTransaction(@NotNull SentryTransaction transaction) { return captureTransaction(transaction, null, null); } } diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index c051b8f39a..ac5af855ca 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -25,27 +25,28 @@ public boolean isEnabled() { } @Override - public SentryId captureEvent(SentryEvent event, @Nullable Object hint) { + public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Object hint) { return SentryId.EMPTY_ID; } @Override - public SentryId captureMessage(String message, SentryLevel level) { + public @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { return SentryId.EMPTY_ID; } @Override - public SentryId captureEnvelope(SentryEnvelope envelope, @Nullable Object hint) { + public @NotNull SentryId captureEnvelope( + @NotNull SentryEnvelope envelope, @Nullable Object hint) { return SentryId.EMPTY_ID; } @Override - public SentryId captureException(Throwable throwable, @Nullable Object hint) { + public @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Object hint) { return SentryId.EMPTY_ID; } @Override - public void captureUserFeedback(UserFeedback userFeedback) {} + public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} @Override public void startSession() {} @@ -57,7 +58,7 @@ public void endSession() {} public void close() {} @Override - public void addBreadcrumb(Breadcrumb breadcrumb, @Nullable Object hint) {} + public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Object hint) {} @Override public void setLevel(SentryLevel level) {} @@ -69,22 +70,22 @@ public void setTransaction(String transaction) {} public void setUser(User user) {} @Override - public void setFingerprint(List fingerprint) {} + public void setFingerprint(@NotNull List fingerprint) {} @Override public void clearBreadcrumbs() {} @Override - public void setTag(String key, String value) {} + public void setTag(@NotNull String key, @NotNull String value) {} @Override - public void removeTag(String key) {} + public void removeTag(@NotNull String key) {} @Override - public void setExtra(String key, String value) {} + public void setExtra(@NotNull String key, @NotNull String value) {} @Override - public void removeExtra(String key) {} + public void removeExtra(@NotNull String key) {} @Override public SentryId getLastEventId() { @@ -98,36 +99,36 @@ public void pushScope() {} public void popScope() {} @Override - public void withScope(ScopeCallback callback) {} + public void withScope(@NotNull ScopeCallback callback) {} @Override - public void configureScope(ScopeCallback callback) {} + public void configureScope(@NotNull ScopeCallback callback) {} @Override - public void bindClient(ISentryClient client) {} + public void bindClient(@NotNull ISentryClient client) {} @Override public void flush(long timeoutMillis) {} @Override - public IHub clone() { + public @NotNull IHub clone() { return instance; } @Override - public SentryId captureTransaction( + public @NotNull SentryId captureTransaction( final @NotNull SentryTransaction transaction, final @Nullable Object hint) { return SentryId.EMPTY_ID; } @Override - public @NotNull ITransaction startTransaction(TransactionContext transactionContexts) { + public @NotNull ITransaction startTransaction(@NotNull TransactionContext transactionContexts) { return NoOpTransaction.getInstance(); } @Override public @NotNull ITransaction startTransaction( - TransactionContext transactionContexts, + @NotNull TransactionContext transactionContexts, CustomSamplingContext customSamplingContext, boolean bindToScope) { return NoOpTransaction.getInstance(); diff --git a/sentry/src/main/java/io/sentry/NoOpSentryClient.java b/sentry/src/main/java/io/sentry/NoOpSentryClient.java index 5119980f75..14eccc68c8 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryClient.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryClient.java @@ -2,6 +2,7 @@ import io.sentry.protocol.SentryId; import io.sentry.protocol.SentryTransaction; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; final class NoOpSentryClient implements ISentryClient { @@ -42,7 +43,8 @@ public SentryId captureEnvelope(SentryEnvelope envelope, @Nullable Object hint) } @Override - public SentryId captureTransaction(SentryTransaction transaction, Scope scope, Object hint) { + public @NotNull SentryId captureTransaction( + @NotNull SentryTransaction transaction, @NotNull Scope scope, @Nullable Object hint) { return SentryId.EMPTY_ID; } } diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index 66593808ef..abc0343497 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -394,7 +394,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Objec @Override public @NotNull SentryId captureTransaction( @NotNull SentryTransaction transaction, - final @Nullable Scope scope, + final @NotNull Scope scope, final @Nullable Object hint) { Objects.requireNonNull(transaction, "Transaction is required."); diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 1a8853e86d..1c1f424507 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -209,7 +209,7 @@ public class SentryOptions { private String distinctId; /** The server name used in the Sentry messages. */ - private String serverName; + private @Nullable String serverName; /** Automatically resolve server name. */ private boolean attachServerName = true; @@ -266,7 +266,7 @@ public class SentryOptions { * deduplication prevents from receiving the same exception multiple times when there is more than * one framework active that captures errors, for example Logback and Spring Boot. */ - private Boolean enableDeduplication = true; + private @Nullable Boolean enableDeduplication = true; /** Maximum number of spans that can be atteched to single transaction. */ private int maxSpans = 1000; @@ -727,7 +727,7 @@ public void setSampleRate(Double sampleRate) { * * @param tracesSampleRate the sample rate */ - public void setTracesSampleRate(Double tracesSampleRate) { + public void setTracesSampleRate(final @Nullable Double tracesSampleRate) { if (tracesSampleRate != null && (tracesSampleRate > 1.0 || tracesSampleRate < 0.0)) { throw new IllegalArgumentException( "The value " diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index f983beb790..2279f657bb 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -128,7 +128,10 @@ void setSampled(final @Nullable Boolean sampled) { @Override public SpanContext clone() throws CloneNotSupportedException { final SpanContext clone = (SpanContext) super.clone(); - clone.tags = CollectionUtils.shallowCopy(tags); + final Map copiedTags = CollectionUtils.shallowCopy(this.tags); + if (copiedTags != null) { + clone.tags = copiedTags; + } return clone; } } diff --git a/sentry/src/test/java/io/sentry/HubTest.kt b/sentry/src/test/java/io/sentry/HubTest.kt index 08b413f437..f4c5429780 100644 --- a/sentry/src/test/java/io/sentry/HubTest.kt +++ b/sentry/src/test/java/io/sentry/HubTest.kt @@ -256,17 +256,6 @@ class HubTest { } //region captureEvent tests - @Test - fun `when captureEvent is called and event is null, lastEventId is empty`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - sut.captureEvent(null) - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - @Test fun `when captureEvent is called on disabled client, do nothing`() { val (sut, mockClient) = getEnabledHub() @@ -356,17 +345,6 @@ class HubTest { //endregion //region captureMessage tests - @Test - fun `when captureMessage is called and event is null, lastEventId is empty`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - sut.captureMessage(null) - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - @Test fun `when captureMessage is called on disabled client, do nothing`() { val (sut, mockClient) = getEnabledHub() @@ -393,17 +371,6 @@ class HubTest { //endregion //region captureException tests - @Test - fun `when captureException is called and exception is null, lastEventId is empty`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - sut.captureException(null) - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - @Test fun `when captureException is called on disabled client, do nothing`() { val (sut, mockClient) = getEnabledHub() @@ -699,18 +666,6 @@ class HubTest { assertEquals(0, scope?.fingerprint?.count()) } - @Test - fun `when setFingerprint is called with null parameter, do nothing`() { - val hub = generateHub() - var scope: Scope? = null - hub.configureScope { - scope = it - } - - hub.setFingerprint(null) - assertEquals(0, scope?.fingerprint?.count()) - } - @Test fun `when setFingerprint is called, fingerprint is set`() { val hub = generateHub() @@ -771,18 +726,6 @@ class HubTest { assertEquals(0, scope?.tags?.count()) } - @Test - fun `when setTag is called with null parameters, do nothing`() { - val hub = generateHub() - var scope: Scope? = null - hub.configureScope { - scope = it - } - - hub.setTag(null, null) - assertEquals(0, scope?.tags?.count()) - } - @Test fun `when setTag is called, tag is set`() { val hub = generateHub() @@ -810,18 +753,6 @@ class HubTest { assertEquals(0, scope?.extras?.count()) } - @Test - fun `when setExtra is called with null parameters, do nothing`() { - val hub = generateHub() - var scope: Scope? = null - hub.configureScope { - scope = it - } - - hub.setExtra(null, null) - assertEquals(0, scope?.extras?.count()) - } - @Test fun `when setExtra is called, extra is set`() { val hub = generateHub() @@ -836,16 +767,6 @@ class HubTest { //endregion //region captureEnvelope tests - @Test - fun `when captureEnvelope is called and envelope is null, throws IllegalArgumentException`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - assertFailsWith { sut.captureEnvelope(null) } - } - @Test fun `when captureEnvelope is called on disabled client, do nothing`() { val options = SentryOptions() diff --git a/sentry/src/test/java/io/sentry/NoOpHubTest.kt b/sentry/src/test/java/io/sentry/NoOpHubTest.kt index f82a082652..d50d10fe8a 100644 --- a/sentry/src/test/java/io/sentry/NoOpHubTest.kt +++ b/sentry/src/test/java/io/sentry/NoOpHubTest.kt @@ -18,39 +18,39 @@ class NoOpHubTest { @Test fun `addBreadcrumb is doesn't throw on null breadcrumb`() = - sut.addBreadcrumb(null) + sut.addBreadcrumb("breadcrumb") @Test fun `hub is always disabled`() = assertFalse(sut.isEnabled) @Test fun `hub is returns empty SentryId`() = - assertEquals(SentryId.EMPTY_ID, sut.captureEvent(null)) + assertEquals(SentryId.EMPTY_ID, sut.captureEvent(SentryEvent())) @Test fun `captureException is returns empty SentryId`() = - assertEquals(SentryId.EMPTY_ID, sut.captureException(null)) + assertEquals(SentryId.EMPTY_ID, sut.captureException(RuntimeException())) @Test fun `captureMessage is returns empty SentryId`() = - assertEquals(SentryId.EMPTY_ID, sut.captureMessage(null)) + assertEquals(SentryId.EMPTY_ID, sut.captureMessage("message")) @Test fun `close does not affect captureEvent`() { sut.close() - assertEquals(SentryId.EMPTY_ID, sut.captureEvent(null)) + assertEquals(SentryId.EMPTY_ID, sut.captureEvent(SentryEvent())) } @Test fun `close does not affect captureException`() { sut.close() - assertEquals(SentryId.EMPTY_ID, sut.captureException(null)) + assertEquals(SentryId.EMPTY_ID, sut.captureException(RuntimeException())) } @Test fun `close does not affect captureMessage`() { sut.close() - assertEquals(SentryId.EMPTY_ID, sut.captureMessage(null)) + assertEquals(SentryId.EMPTY_ID, sut.captureMessage("message")) } @Test @@ -59,15 +59,6 @@ class NoOpHubTest { @Test fun `popScope is no op`() = sut.popScope() - @Test - fun `bindClient doesn't throw on null param`() = sut.bindClient(null) - - @Test - fun `withScope doesn't throw on null param`() = sut.withScope(null) - - @Test - fun `configureScope doesn't throw on null param`() = sut.configureScope(null) - @Test fun `flush doesn't throw on null param`() = sut.flush(30000) diff --git a/sentry/src/test/java/io/sentry/NoOpSentryClientTest.kt b/sentry/src/test/java/io/sentry/NoOpSentryClientTest.kt index c374a3ae15..ed436db5d1 100644 --- a/sentry/src/test/java/io/sentry/NoOpSentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/NoOpSentryClientTest.kt @@ -1,5 +1,6 @@ package io.sentry +import com.nhaarman.mockitokotlin2.mock import io.sentry.protocol.SentryId import kotlin.test.Test import kotlin.test.assertEquals @@ -43,5 +44,5 @@ class NoOpSentryClientTest { @Test fun `captureTransaction returns empty SentryId`() = - assertEquals(SentryId.EMPTY_ID, sut.captureTransaction(null)) + assertEquals(SentryId.EMPTY_ID, sut.captureTransaction(mock())) } From 69f707e183743976666115f2a63f03290f3bd56d Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 30 Apr 2021 09:49:52 +0200 Subject: [PATCH 15/40] Add nullability annotations to `ISerializer` --- sentry/src/main/java/io/sentry/GsonSerializer.java | 2 +- sentry/src/main/java/io/sentry/ISerializer.java | 13 ++++++++----- sentry/src/main/java/io/sentry/NoOpSerializer.java | 14 ++++++++------ .../src/test/java/io/sentry/NoOpSerializerTest.kt | 11 ++--------- sentry/src/test/java/io/sentry/SentryClientTest.kt | 4 ++-- .../test/java/io/sentry/cache/CacheStrategyTest.kt | 4 ++-- 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/sentry/src/main/java/io/sentry/GsonSerializer.java b/sentry/src/main/java/io/sentry/GsonSerializer.java index cd20495445..a8930039be 100644 --- a/sentry/src/main/java/io/sentry/GsonSerializer.java +++ b/sentry/src/main/java/io/sentry/GsonSerializer.java @@ -197,7 +197,7 @@ public void serialize( * @throws Exception the Exception if there was a problem during serialization */ @Override - public String serialize(final @NotNull Map data) throws Exception { + public @NotNull String serialize(final @NotNull Map data) throws Exception { Objects.requireNonNull(data, "The SentryEnvelope object is required."); return gson.toJson(data); diff --git a/sentry/src/main/java/io/sentry/ISerializer.java b/sentry/src/main/java/io/sentry/ISerializer.java index c1c6b80aba..cf80288e3a 100644 --- a/sentry/src/main/java/io/sentry/ISerializer.java +++ b/sentry/src/main/java/io/sentry/ISerializer.java @@ -1,5 +1,8 @@ package io.sentry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -8,13 +11,13 @@ import java.util.Map; public interface ISerializer { - T deserialize(Reader reader, Class clazz); + @Nullable T deserialize(@NotNull Reader reader, @NotNull Class clazz); - SentryEnvelope deserializeEnvelope(InputStream inputStream); + @Nullable SentryEnvelope deserializeEnvelope(@NotNull InputStream inputStream); - void serialize(T entity, Writer writer) throws IOException; + void serialize(@NotNull T entity, @NotNull Writer writer) throws IOException; - void serialize(SentryEnvelope envelope, OutputStream outputStream) throws Exception; + void serialize(@NotNull SentryEnvelope envelope, @NotNull OutputStream outputStream) throws Exception; - String serialize(Map data) throws Exception; + @NotNull String serialize(@NotNull Map data) throws Exception; } diff --git a/sentry/src/main/java/io/sentry/NoOpSerializer.java b/sentry/src/main/java/io/sentry/NoOpSerializer.java index d8e770f628..79eb8264b7 100644 --- a/sentry/src/main/java/io/sentry/NoOpSerializer.java +++ b/sentry/src/main/java/io/sentry/NoOpSerializer.java @@ -6,6 +6,8 @@ import java.io.Reader; import java.io.Writer; import java.util.Map; + +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** No-op implementation of ISerializer */ @@ -20,23 +22,23 @@ public static NoOpSerializer getInstance() { private NoOpSerializer() {} @Override - public T deserialize(Reader reader, Class clazz) { + public @Nullable T deserialize(@NotNull Reader reader, @NotNull Class clazz) { return null; } @Override - public @Nullable SentryEnvelope deserializeEnvelope(InputStream inputStream) { + public @Nullable SentryEnvelope deserializeEnvelope(@NotNull InputStream inputStream) { return null; } @Override - public void serialize(T entity, Writer writer) throws IOException {} + public void serialize(@NotNull T entity, @NotNull Writer writer) throws IOException {} @Override - public void serialize(SentryEnvelope envelope, OutputStream outputStream) throws Exception {} + public void serialize(@NotNull SentryEnvelope envelope, @NotNull OutputStream outputStream) throws Exception {} @Override - public @Nullable String serialize(Map data) throws Exception { - return null; + public @NotNull String serialize(@NotNull Map data) throws Exception { + return ""; } } diff --git a/sentry/src/test/java/io/sentry/NoOpSerializerTest.kt b/sentry/src/test/java/io/sentry/NoOpSerializerTest.kt index 5be69313ac..c0c328c3db 100644 --- a/sentry/src/test/java/io/sentry/NoOpSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/NoOpSerializerTest.kt @@ -1,21 +1,14 @@ package io.sentry +import com.nhaarman.mockitokotlin2.mock import kotlin.test.Test import kotlin.test.assertEquals class NoOpSerializerTest { private val sut: NoOpSerializer = NoOpSerializer.getInstance() - @Test - fun `serialize doesn't throw on null params`() = sut.serialize(null as SentryEvent?, null) - - @Test - fun `deserializeEvent doesn't throw on null param`() { - sut.deserialize(null, SentryEvent::class.java) - } - @Test fun `deserializeEvent returns null on NoOp`() { - assertEquals(null, sut.deserialize(null, SentryEvent::class.java)) + assertEquals(null, sut.deserialize(mock(), SentryEvent::class.java)) } } diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 6cdf6ee051..17e37708b6 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -1059,12 +1059,12 @@ class SentryClientTest { private fun getEventFromData(data: ByteArray): SentryEvent { val inputStream = InputStreamReader(ByteArrayInputStream(data)) - return fixture.sentryOptions.serializer.deserialize(inputStream, SentryEvent::class.java) + return fixture.sentryOptions.serializer.deserialize(inputStream, SentryEvent::class.java)!! } private fun getTransactionFromData(data: ByteArray): SentryTransaction { val inputStream = InputStreamReader(ByteArrayInputStream(data)) - return fixture.sentryOptions.serializer.deserialize(inputStream, SentryTransaction::class.java) + return fixture.sentryOptions.serializer.deserialize(inputStream, SentryTransaction::class.java)!! } private fun verifyAttachmentsInEnvelope(eventId: SentryId?) { diff --git a/sentry/src/test/java/io/sentry/cache/CacheStrategyTest.kt b/sentry/src/test/java/io/sentry/cache/CacheStrategyTest.kt index 33d502212f..f9027762ff 100644 --- a/sentry/src/test/java/io/sentry/cache/CacheStrategyTest.kt +++ b/sentry/src/test/java/io/sentry/cache/CacheStrategyTest.kt @@ -163,10 +163,10 @@ class CacheStrategyTest { private fun getSessionFromFile(file: File, sut: CacheStrategy): Session { val envelope = sut.serializer.deserializeEnvelope(file.inputStream()) - val item = envelope.items.first() + val item = envelope!!.items.first() val reader = InputStreamReader(ByteArrayInputStream(item.data), Charsets.UTF_8) - return sut.serializer.deserialize(reader, Session::class.java) + return sut.serializer.deserialize(reader, Session::class.java)!! } private fun saveSessionToFile(file: File, sut: CacheStrategy, state: Session.State = Session.State.Ok, init: Boolean? = true) { From 98f50498a5da6bb49fa524e0d96368a18e2cda9e Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 30 Apr 2021 09:57:46 +0200 Subject: [PATCH 16/40] Add nullability annotations. --- sentry/src/main/java/io/sentry/ShutdownHookIntegration.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java index afb1d1390e..be60a3ff05 100644 --- a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java +++ b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java @@ -4,6 +4,7 @@ import java.io.Closeable; import java.io.IOException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import org.jetbrains.annotations.VisibleForTesting; @@ -12,7 +13,7 @@ public final class ShutdownHookIntegration implements Integration, Closeable { private final @NotNull Runtime runtime; - private @NotNull Thread thread; + private @Nullable Thread thread; @TestOnly public ShutdownHookIntegration(final @NotNull Runtime runtime) { From ef5dcdea67ec3a88c2a66f570dcbe3f5b5ef415a Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 30 Apr 2021 10:03:45 +0200 Subject: [PATCH 17/40] Add nullability annotations to `adapters` package. (#1448) --- .../main/java/io/sentry/adapters/CollectionAdapter.java | 2 +- .../io/sentry/adapters/ContextsDeserializerAdapter.java | 5 ++++- .../java/io/sentry/adapters/ContextsSerializerAdapter.java | 6 +++++- .../java/io/sentry/adapters/DateDeserializerAdapter.java | 6 +++++- .../java/io/sentry/adapters/DateSerializerAdapter.java | 6 +++++- sentry/src/main/java/io/sentry/adapters/MapAdapter.java | 2 +- .../io/sentry/adapters/OrientationDeserializerAdapter.java | 7 +++++-- .../io/sentry/adapters/OrientationSerializerAdapter.java | 7 +++++-- .../io/sentry/adapters/SentryIdDeserializerAdapter.java | 6 +++++- .../java/io/sentry/adapters/SentryIdSerializerAdapter.java | 6 +++++- .../io/sentry/adapters/SentryLevelDeserializerAdapter.java | 6 +++++- .../io/sentry/adapters/SentryLevelSerializerAdapter.java | 6 +++++- .../java/io/sentry/adapters/SpanIdDeserializerAdapter.java | 6 +++++- .../java/io/sentry/adapters/SpanIdSerializerAdapter.java | 6 +++++- .../io/sentry/adapters/SpanStatusDeserializerAdapter.java | 6 +++++- .../io/sentry/adapters/SpanStatusSerializerAdapter.java | 6 +++++- .../io/sentry/adapters/TimeZoneDeserializerAdapter.java | 6 +++++- .../java/io/sentry/adapters/TimeZoneSerializerAdapter.java | 6 +++++- 18 files changed, 81 insertions(+), 20 deletions(-) diff --git a/sentry/src/main/java/io/sentry/adapters/CollectionAdapter.java b/sentry/src/main/java/io/sentry/adapters/CollectionAdapter.java index f7ab4bda0a..ae08f23df4 100644 --- a/sentry/src/main/java/io/sentry/adapters/CollectionAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/CollectionAdapter.java @@ -16,7 +16,7 @@ @ApiStatus.Internal public final class CollectionAdapter implements JsonSerializer> { @Override - public JsonElement serialize( + public @Nullable JsonElement serialize( final @Nullable Collection src, final @NotNull Type typeOfSrc, final @NotNull JsonSerializationContext context) { diff --git a/sentry/src/main/java/io/sentry/adapters/ContextsDeserializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/ContextsDeserializerAdapter.java index 99542cc506..bba24b1370 100644 --- a/sentry/src/main/java/io/sentry/adapters/ContextsDeserializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/ContextsDeserializerAdapter.java @@ -30,7 +30,10 @@ public ContextsDeserializerAdapter(final @NotNull SentryOptions options) { } @Override - public Contexts deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public @Nullable Contexts deserialize( + final @Nullable JsonElement json, + final @NotNull Type typeOfT, + final @NotNull JsonDeserializationContext context) throws JsonParseException { try { if (json != null && !json.isJsonNull()) { diff --git a/sentry/src/main/java/io/sentry/adapters/ContextsSerializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/ContextsSerializerAdapter.java index f7ec6b7061..ff1e632871 100644 --- a/sentry/src/main/java/io/sentry/adapters/ContextsSerializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/ContextsSerializerAdapter.java @@ -12,6 +12,7 @@ import java.util.Map; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class ContextsSerializerAdapter implements JsonSerializer { @@ -23,7 +24,10 @@ public ContextsSerializerAdapter(final @NotNull SentryOptions options) { } @Override - public JsonElement serialize(Contexts src, Type typeOfSrc, JsonSerializationContext context) { + public @Nullable JsonElement serialize( + final @Nullable Contexts src, + final @NotNull Type typeOfSrc, + final @NotNull JsonSerializationContext context) { if (src == null) { return null; } diff --git a/sentry/src/main/java/io/sentry/adapters/DateDeserializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/DateDeserializerAdapter.java index 5ff6f5b1e3..a0dc81c045 100644 --- a/sentry/src/main/java/io/sentry/adapters/DateDeserializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/DateDeserializerAdapter.java @@ -11,6 +11,7 @@ import java.util.Date; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class DateDeserializerAdapter implements JsonDeserializer { @@ -22,7 +23,10 @@ public DateDeserializerAdapter(final @NotNull SentryOptions options) { } @Override - public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public @Nullable Date deserialize( + final @Nullable JsonElement json, + final @NotNull Type typeOfT, + final @NotNull JsonDeserializationContext context) throws JsonParseException { try { return json == null ? null : DateUtils.getDateTime(json.getAsString()); diff --git a/sentry/src/main/java/io/sentry/adapters/DateSerializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/DateSerializerAdapter.java index c38e64a7c8..5e5823ca3f 100644 --- a/sentry/src/main/java/io/sentry/adapters/DateSerializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/DateSerializerAdapter.java @@ -11,6 +11,7 @@ import java.util.Date; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class DateSerializerAdapter implements JsonSerializer { @@ -22,7 +23,10 @@ public DateSerializerAdapter(final @NotNull SentryOptions options) { } @Override - public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { + public @Nullable JsonElement serialize( + final @Nullable Date src, + final @NotNull Type typeOfSrc, + final @NotNull JsonSerializationContext context) { try { return src == null ? null : new JsonPrimitive(DateUtils.getTimestamp(src)); } catch (Exception e) { diff --git a/sentry/src/main/java/io/sentry/adapters/MapAdapter.java b/sentry/src/main/java/io/sentry/adapters/MapAdapter.java index f7428ca031..b244e72747 100644 --- a/sentry/src/main/java/io/sentry/adapters/MapAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/MapAdapter.java @@ -14,7 +14,7 @@ @ApiStatus.Internal public final class MapAdapter implements JsonSerializer> { @Override - public JsonElement serialize( + public @Nullable JsonElement serialize( final @Nullable Map src, final @NotNull Type typeOfSrc, final @NotNull JsonSerializationContext context) { diff --git a/sentry/src/main/java/io/sentry/adapters/OrientationDeserializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/OrientationDeserializerAdapter.java index d70e5a60d7..b0ed489af5 100644 --- a/sentry/src/main/java/io/sentry/adapters/OrientationDeserializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/OrientationDeserializerAdapter.java @@ -11,6 +11,7 @@ import java.util.Locale; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class OrientationDeserializerAdapter @@ -23,8 +24,10 @@ public OrientationDeserializerAdapter(final @NotNull SentryOptions options) { } @Override - public Device.DeviceOrientation deserialize( - JsonElement json, Type typeOfT, JsonDeserializationContext context) + public @Nullable Device.DeviceOrientation deserialize( + final @Nullable JsonElement json, + final @NotNull Type typeOfT, + final @NotNull JsonDeserializationContext context) throws JsonParseException { try { return json == null diff --git a/sentry/src/main/java/io/sentry/adapters/OrientationSerializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/OrientationSerializerAdapter.java index 0e27c68ff3..21d594db49 100644 --- a/sentry/src/main/java/io/sentry/adapters/OrientationSerializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/OrientationSerializerAdapter.java @@ -11,6 +11,7 @@ import java.util.Locale; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class OrientationSerializerAdapter @@ -23,8 +24,10 @@ public OrientationSerializerAdapter(final @NotNull SentryOptions options) { } @Override - public JsonElement serialize( - Device.DeviceOrientation src, Type typeOfSrc, JsonSerializationContext context) { + public @Nullable JsonElement serialize( + final @Nullable Device.DeviceOrientation src, + final @NotNull Type typeOfSrc, + final @NotNull JsonSerializationContext context) { try { return src == null ? null : new JsonPrimitive(src.name().toLowerCase(Locale.ROOT)); } catch (Exception e) { diff --git a/sentry/src/main/java/io/sentry/adapters/SentryIdDeserializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/SentryIdDeserializerAdapter.java index e9def47b6f..15003bdf37 100644 --- a/sentry/src/main/java/io/sentry/adapters/SentryIdDeserializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/SentryIdDeserializerAdapter.java @@ -10,6 +10,7 @@ import java.lang.reflect.Type; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SentryIdDeserializerAdapter implements JsonDeserializer { @@ -21,7 +22,10 @@ public SentryIdDeserializerAdapter(final @NotNull SentryOptions options) { } @Override - public SentryId deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public @Nullable SentryId deserialize( + final @Nullable JsonElement json, + final @NotNull Type typeOfT, + final @NotNull JsonDeserializationContext context) throws JsonParseException { try { return json == null ? null : new SentryId(json.getAsString()); diff --git a/sentry/src/main/java/io/sentry/adapters/SentryIdSerializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/SentryIdSerializerAdapter.java index ac10c47e0d..5ae988981e 100644 --- a/sentry/src/main/java/io/sentry/adapters/SentryIdSerializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/SentryIdSerializerAdapter.java @@ -10,6 +10,7 @@ import java.lang.reflect.Type; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SentryIdSerializerAdapter implements JsonSerializer { @@ -21,7 +22,10 @@ public SentryIdSerializerAdapter(final @NotNull SentryOptions options) { } @Override - public JsonElement serialize(SentryId src, Type typeOfSrc, JsonSerializationContext context) { + public @Nullable JsonElement serialize( + final @Nullable SentryId src, + final @NotNull Type typeOfSrc, + final @NotNull JsonSerializationContext context) { try { return src == null ? null : new JsonPrimitive(src.toString()); } catch (Exception e) { diff --git a/sentry/src/main/java/io/sentry/adapters/SentryLevelDeserializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/SentryLevelDeserializerAdapter.java index 5703fc4406..8e0267ea6b 100644 --- a/sentry/src/main/java/io/sentry/adapters/SentryLevelDeserializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/SentryLevelDeserializerAdapter.java @@ -10,6 +10,7 @@ import java.util.Locale; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SentryLevelDeserializerAdapter implements JsonDeserializer { @@ -21,7 +22,10 @@ public SentryLevelDeserializerAdapter(final @NotNull SentryOptions options) { } @Override - public SentryLevel deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public @Nullable SentryLevel deserialize( + final @Nullable JsonElement json, + final @NotNull Type typeOfT, + final @NotNull JsonDeserializationContext context) throws JsonParseException { try { return json == null ? null : SentryLevel.valueOf(json.getAsString().toUpperCase(Locale.ROOT)); diff --git a/sentry/src/main/java/io/sentry/adapters/SentryLevelSerializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/SentryLevelSerializerAdapter.java index 7f044ec7a8..1086659ad3 100644 --- a/sentry/src/main/java/io/sentry/adapters/SentryLevelSerializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/SentryLevelSerializerAdapter.java @@ -10,6 +10,7 @@ import java.util.Locale; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SentryLevelSerializerAdapter implements JsonSerializer { @@ -21,7 +22,10 @@ public SentryLevelSerializerAdapter(final @NotNull SentryOptions options) { } @Override - public JsonElement serialize(SentryLevel src, Type typeOfSrc, JsonSerializationContext context) { + public @Nullable JsonElement serialize( + final @Nullable SentryLevel src, + final @NotNull Type typeOfSrc, + final @NotNull JsonSerializationContext context) { try { return src == null ? null : new JsonPrimitive(src.name().toLowerCase(Locale.ROOT)); } catch (Exception e) { diff --git a/sentry/src/main/java/io/sentry/adapters/SpanIdDeserializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/SpanIdDeserializerAdapter.java index 3c076e3e11..73a93b342f 100644 --- a/sentry/src/main/java/io/sentry/adapters/SpanIdDeserializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/SpanIdDeserializerAdapter.java @@ -10,6 +10,7 @@ import java.lang.reflect.Type; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SpanIdDeserializerAdapter implements JsonDeserializer { @@ -21,7 +22,10 @@ public SpanIdDeserializerAdapter(final @NotNull SentryOptions options) { } @Override - public SpanId deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public @Nullable SpanId deserialize( + final @Nullable JsonElement json, + final @Nullable Type typeOfT, + final @NotNull JsonDeserializationContext context) throws JsonParseException { try { return json == null ? null : new SpanId(json.getAsString()); diff --git a/sentry/src/main/java/io/sentry/adapters/SpanIdSerializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/SpanIdSerializerAdapter.java index 8b6a36cd9c..4f01460c38 100644 --- a/sentry/src/main/java/io/sentry/adapters/SpanIdSerializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/SpanIdSerializerAdapter.java @@ -10,6 +10,7 @@ import java.lang.reflect.Type; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SpanIdSerializerAdapter implements JsonSerializer { @@ -21,7 +22,10 @@ public SpanIdSerializerAdapter(final @NotNull SentryOptions options) { } @Override - public JsonElement serialize(SpanId src, Type typeOfSrc, JsonSerializationContext context) { + public @Nullable JsonElement serialize( + final @Nullable SpanId src, + final @NotNull Type typeOfSrc, + final @NotNull JsonSerializationContext context) { try { return src == null ? null : new JsonPrimitive(src.toString()); } catch (Exception e) { diff --git a/sentry/src/main/java/io/sentry/adapters/SpanStatusDeserializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/SpanStatusDeserializerAdapter.java index 393d804a72..cc1b7d4c34 100644 --- a/sentry/src/main/java/io/sentry/adapters/SpanStatusDeserializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/SpanStatusDeserializerAdapter.java @@ -11,6 +11,7 @@ import java.util.Locale; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SpanStatusDeserializerAdapter implements JsonDeserializer { @@ -22,7 +23,10 @@ public SpanStatusDeserializerAdapter(final @NotNull SentryOptions options) { } @Override - public SpanStatus deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public @Nullable SpanStatus deserialize( + final @Nullable JsonElement json, + final @NotNull Type typeOfT, + final @NotNull JsonDeserializationContext context) throws JsonParseException { try { return json == null ? null : SpanStatus.valueOf(json.getAsString().toUpperCase(Locale.ROOT)); diff --git a/sentry/src/main/java/io/sentry/adapters/SpanStatusSerializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/SpanStatusSerializerAdapter.java index 7b729c0c2c..296db3f48b 100644 --- a/sentry/src/main/java/io/sentry/adapters/SpanStatusSerializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/SpanStatusSerializerAdapter.java @@ -11,6 +11,7 @@ import java.util.Locale; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SpanStatusSerializerAdapter implements JsonSerializer { @@ -22,7 +23,10 @@ public SpanStatusSerializerAdapter(final @NotNull SentryOptions options) { } @Override - public JsonElement serialize(SpanStatus src, Type typeOfSrc, JsonSerializationContext context) { + public @Nullable JsonElement serialize( + final @Nullable SpanStatus src, + final @NotNull Type typeOfSrc, + final @NotNull JsonSerializationContext context) { try { return src == null ? null : new JsonPrimitive(src.name().toLowerCase(Locale.ROOT)); } catch (Exception e) { diff --git a/sentry/src/main/java/io/sentry/adapters/TimeZoneDeserializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/TimeZoneDeserializerAdapter.java index 5cdbd08a06..26fd12504e 100644 --- a/sentry/src/main/java/io/sentry/adapters/TimeZoneDeserializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/TimeZoneDeserializerAdapter.java @@ -10,6 +10,7 @@ import java.util.TimeZone; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class TimeZoneDeserializerAdapter implements JsonDeserializer { @@ -21,7 +22,10 @@ public TimeZoneDeserializerAdapter(final @NotNull SentryOptions options) { } @Override - public TimeZone deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public @Nullable TimeZone deserialize( + final @Nullable JsonElement json, + final @NotNull Type typeOfT, + final @NotNull JsonDeserializationContext context) throws JsonParseException { try { return json == null ? null : TimeZone.getTimeZone(json.getAsString()); diff --git a/sentry/src/main/java/io/sentry/adapters/TimeZoneSerializerAdapter.java b/sentry/src/main/java/io/sentry/adapters/TimeZoneSerializerAdapter.java index cd1934bf5a..29feb1f419 100644 --- a/sentry/src/main/java/io/sentry/adapters/TimeZoneSerializerAdapter.java +++ b/sentry/src/main/java/io/sentry/adapters/TimeZoneSerializerAdapter.java @@ -10,6 +10,7 @@ import java.util.TimeZone; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class TimeZoneSerializerAdapter implements JsonSerializer { @@ -21,7 +22,10 @@ public TimeZoneSerializerAdapter(final @NotNull SentryOptions options) { } @Override - public JsonElement serialize(TimeZone src, Type typeOfSrc, JsonSerializationContext context) { + public @Nullable JsonElement serialize( + final @Nullable TimeZone src, + final @NotNull Type typeOfSrc, + final @NotNull JsonSerializationContext context) { try { return src == null ? null : new JsonPrimitive(src.getID()); } catch (Exception e) { From c4daf63dfe89a088a9739d03717a1112d92930ff Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 30 Apr 2021 12:56:48 +0200 Subject: [PATCH 18/40] Add nullability annotations to `SentryEvent`, `SentryBaseEvent`. --- buildSrc/src/main/java/Config.kt | 1 + .../core/DefaultAndroidEventProcessorTest.kt | 28 ++++++--- .../android/timber/SentryTimberTreeTest.kt | 5 +- .../kotlin/io/sentry/jul/SentryHandlerTest.kt | 52 ++++++++------- .../io/sentry/log4j2/SentryAppenderTest.kt | 44 +++++++------ .../io/sentry/logback/SentryAppenderTest.kt | 44 +++++++------ .../boot/it/SentrySpringIntegrationTest.kt | 3 +- sentry/build.gradle.kts | 11 ++-- .../src/main/java/io/sentry/ISerializer.java | 14 +++-- .../main/java/io/sentry/NoOpSerializer.java | 4 +- .../main/java/io/sentry/SentryBaseEvent.java | 48 +++++++------- .../src/main/java/io/sentry/SentryEvent.java | 63 ++++++++++--------- .../test/java/io/sentry/GsonSerializerTest.kt | 47 ++++++++------ .../java/io/sentry/MainEventProcessorTest.kt | 18 +++--- .../test/java/io/sentry/SentryClientTest.kt | 46 +++++++++----- .../test/java/io/sentry/SentryEventTest.kt | 5 +- 16 files changed, 251 insertions(+), 182 deletions(-) diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index c346862f3e..f208ee8238 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -101,6 +101,7 @@ object Config { val mockitoInline = "org.mockito:mockito-inline:3.8.0" val awaitility = "org.awaitility:awaitility-kotlin:4.0.3" val mockWebserver = "com.squareup.okhttp3:mockwebserver:4.9.0" + val jsonUnit = "net.javacrumbs.json-unit:json-unit:2.11.1" } object QualityPlugins { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt index 65fa6bc68b..09a66251e9 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt @@ -107,8 +107,10 @@ class DefaultAndroidEventProcessorTest { assertNotNull(sut.process(SentryEvent(), null)) { assertNotNull(it.contexts.app) assertNotNull(it.dist) - assertNotNull(it.debugMeta.images) { images -> - assertEquals("test", images[0].uuid) + assertNotNull(it.debugMeta) { debugMeta -> + assertNotNull(debugMeta.images) { images -> + assertEquals("test", images[0].uuid) + } } } } @@ -131,8 +133,10 @@ class DefaultAndroidEventProcessorTest { } assertNotNull(sut.process(event, null)) { - assertNotNull(it.debugMeta.images) { images -> - assertEquals("test", images[0].uuid) + assertNotNull(it.debugMeta) { debugMeta -> + assertNotNull(debugMeta.images) { images -> + assertEquals("test", images[0].uuid) + } } } } @@ -152,9 +156,11 @@ class DefaultAndroidEventProcessorTest { } assertNotNull(sut.process(event, null)) { - assertNotNull(it.debugMeta.images) { images -> - assertEquals("abc", images.first().uuid) - assertEquals("test", images.last().uuid) + assertNotNull(it.debugMeta) { debugMeta -> + assertNotNull(debugMeta.images) { images -> + assertEquals("abc", images.first().uuid) + assertEquals("test", images.last().uuid) + } } } } @@ -171,7 +177,9 @@ class DefaultAndroidEventProcessorTest { } assertNotNull(sut.process(event, null)) { - assertTrue(it.threads.first().isCurrent == true) + assertNotNull(it.threads) { threads -> + assertTrue(threads.first().isCurrent == true) + } } } @@ -186,7 +194,9 @@ class DefaultAndroidEventProcessorTest { } assertNotNull(sut.process(event, null)) { - assertFalse(it.threads.first().isCurrent == true) + assertNotNull(it.threads) { threads -> + assertFalse(threads.first().isCurrent == true) + } } } diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt index ee6dbe92a5..6448abd74f 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt @@ -12,6 +12,7 @@ import io.sentry.getExc import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue import timber.log.Timber @@ -165,7 +166,9 @@ class SentryTimberTreeTest { val sut = fixture.getSut() sut.e("message") verify(fixture.hub).captureEvent(check { - assertEquals("message", it.message.formatted) + assertNotNull(it.message) { message -> + assertEquals("message", message.formatted) + } }) } diff --git a/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt b/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt index f69390c9d5..4a13870826 100644 --- a/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt +++ b/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt @@ -80,9 +80,11 @@ class SentryHandlerTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals("testing message conversion 1, 2", event.message.formatted) - assertEquals("testing message conversion {0}, {1}", event.message.message) - assertEquals(listOf("1", "2"), event.message.params) + assertNotNull(event.message) { message -> + assertEquals("testing message conversion 1, 2", message.formatted) + assertEquals("testing message conversion {0}, {1}", message.message) + assertEquals(listOf("1", "2"), message.params) + } assertEquals("jul.SentryHandlerTest", event.logger) }, anyOrNull()) } @@ -95,9 +97,11 @@ class SentryHandlerTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals("testing message conversion 1, 2", event.message.formatted) - assertEquals("testing message conversion {0}, {1}", event.message.message) - assertEquals(listOf("1", "2"), event.message.params) + assertNotNull(event.message) { message -> + assertEquals("testing message conversion 1, 2", message.formatted) + assertEquals("testing message conversion {0}, {1}", message.message) + assertEquals(listOf("1", "2"), message.params) + } assertEquals("jul.SentryHandlerTest", event.logger) }, anyOrNull()) } @@ -205,16 +209,18 @@ class SentryHandlerTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(2, event.breadcrumbs.size) - val breadcrumb = event.breadcrumbs[0] - val breadcrumbTime = Instant.ofEpochMilli(event.timestamp.time) - .atZone(ZoneId.of("UTC")) - .toLocalDateTime() - assertTrue { breadcrumbTime.plusSeconds(1).isAfter(utcTime) } - assertTrue { breadcrumbTime.minusSeconds(1).isBefore(utcTime) } - assertEquals("this should be a breadcrumb #1", breadcrumb.message) - assertEquals("jul.SentryHandlerTest", breadcrumb.category) - assertEquals(SentryLevel.DEBUG, breadcrumb.level) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(2, breadcrumbs.size) + val breadcrumb = breadcrumbs[0] + val breadcrumbTime = Instant.ofEpochMilli(event.timestamp.time) + .atZone(ZoneId.of("UTC")) + .toLocalDateTime() + assertTrue { breadcrumbTime.plusSeconds(1).isAfter(utcTime) } + assertTrue { breadcrumbTime.minusSeconds(1).isBefore(utcTime) } + assertEquals("this should be a breadcrumb #1", breadcrumb.message) + assertEquals("jul.SentryHandlerTest", breadcrumb.category) + assertEquals(SentryLevel.DEBUG, breadcrumb.level) + } }, anyOrNull()) } } @@ -229,8 +235,10 @@ class SentryHandlerTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(1, event.breadcrumbs.size) - assertEquals("this should be a breadcrumb", event.breadcrumbs[0].message) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(1, breadcrumbs.size) + assertEquals("this should be a breadcrumb", breadcrumbs[0].message) + } }, anyOrNull()) } } @@ -246,9 +254,11 @@ class SentryHandlerTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(2, event.breadcrumbs.size) - assertEquals("this should be a breadcrumb", event.breadcrumbs[0].message) - assertEquals("this should not be sent as the event but be a breadcrumb", event.breadcrumbs[1].message) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(2, breadcrumbs.size) + assertEquals("this should be a breadcrumb", breadcrumbs[0].message) + assertEquals("this should not be sent as the event but be a breadcrumb", breadcrumbs[1].message) + } }, anyOrNull()) } } diff --git a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt index eafa2ee37b..1629757db1 100644 --- a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt +++ b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt @@ -103,9 +103,11 @@ class SentryAppenderTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals("testing message conversion 1, 2", event.message.formatted) - assertEquals("testing message conversion {}, {}", event.message.message) - assertEquals(listOf("1", "2"), event.message.params) + assertNotNull(event.message) { message -> + assertEquals("testing message conversion 1, 2", message.formatted) + assertEquals("testing message conversion {}, {}", message.message) + assertEquals(listOf("1", "2"), message.params) + } assertEquals("io.sentry.log4j2.SentryAppenderTest", event.logger) }, anyOrNull()) } @@ -270,16 +272,18 @@ class SentryAppenderTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(2, event.breadcrumbs.size) - val breadcrumb = event.breadcrumbs[0] - val breadcrumbTime = Instant.ofEpochMilli(event.timestamp.time) - .atZone(fixture.utcTimeZone) - .toLocalDateTime() - assertTrue { breadcrumbTime.plusSeconds(1).isAfter(utcTime) } - assertTrue { breadcrumbTime.minusSeconds(1).isBefore(utcTime) } - assertEquals("this should be a breadcrumb #1", breadcrumb.message) - assertEquals("io.sentry.log4j2.SentryAppenderTest", breadcrumb.category) - assertEquals(SentryLevel.DEBUG, breadcrumb.level) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(2, breadcrumbs.size) + val breadcrumb = breadcrumbs[0] + val breadcrumbTime = Instant.ofEpochMilli(event.timestamp.time) + .atZone(fixture.utcTimeZone) + .toLocalDateTime() + assertTrue { breadcrumbTime.plusSeconds(1).isAfter(utcTime) } + assertTrue { breadcrumbTime.minusSeconds(1).isBefore(utcTime) } + assertEquals("this should be a breadcrumb #1", breadcrumb.message) + assertEquals("io.sentry.log4j2.SentryAppenderTest", breadcrumb.category) + assertEquals(SentryLevel.DEBUG, breadcrumb.level) + } }, anyOrNull()) } } @@ -294,8 +298,10 @@ class SentryAppenderTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(1, event.breadcrumbs.size) - assertEquals("this should be a breadcrumb", event.breadcrumbs[0].message) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(1, breadcrumbs.size) + assertEquals("this should be a breadcrumb", breadcrumbs[0].message) + } }, anyOrNull()) } } @@ -311,9 +317,11 @@ class SentryAppenderTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(2, event.breadcrumbs.size) - assertEquals("this should be a breadcrumb", event.breadcrumbs[0].message) - assertEquals("this should not be sent as the event but be a breadcrumb", event.breadcrumbs[1].message) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(2, breadcrumbs.size) + assertEquals("this should be a breadcrumb", breadcrumbs[0].message) + assertEquals("this should not be sent as the event but be a breadcrumb", breadcrumbs[1].message) + } }, anyOrNull()) } } diff --git a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt index a4baadcf95..8b2789cb14 100644 --- a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt +++ b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt @@ -93,9 +93,11 @@ class SentryAppenderTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals("testing message conversion 1, 2", event.message.formatted) - assertEquals("testing message conversion {}, {}", event.message.message) - assertEquals(listOf("1", "2"), event.message.params) + assertNotNull(event.message) { message -> + assertEquals("testing message conversion 1, 2", message.formatted) + assertEquals("testing message conversion {}, {}", message.message) + assertEquals(listOf("1", "2"), message.params) + } assertEquals("io.sentry.logback.SentryAppenderTest", event.logger) }, anyOrNull()) } @@ -262,16 +264,18 @@ class SentryAppenderTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(2, event.breadcrumbs.size) - val breadcrumb = event.breadcrumbs[0] - val breadcrumbTime = Instant.ofEpochMilli(event.timestamp.time) - .atZone(fixture.utcTimeZone) - .toLocalDateTime() - assertTrue { breadcrumbTime.plusSeconds(1).isAfter(utcTime) } - assertTrue { breadcrumbTime.minusSeconds(1).isBefore(utcTime) } - assertEquals("this should be a breadcrumb #1", breadcrumb.message) - assertEquals("io.sentry.logback.SentryAppenderTest", breadcrumb.category) - assertEquals(SentryLevel.DEBUG, breadcrumb.level) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(2, breadcrumbs.size) + val breadcrumb = breadcrumbs[0] + val breadcrumbTime = Instant.ofEpochMilli(event.timestamp.time) + .atZone(fixture.utcTimeZone) + .toLocalDateTime() + assertTrue { breadcrumbTime.plusSeconds(1).isAfter(utcTime) } + assertTrue { breadcrumbTime.minusSeconds(1).isBefore(utcTime) } + assertEquals("this should be a breadcrumb #1", breadcrumb.message) + assertEquals("io.sentry.logback.SentryAppenderTest", breadcrumb.category) + assertEquals(SentryLevel.DEBUG, breadcrumb.level) + } }, anyOrNull()) } } @@ -286,8 +290,10 @@ class SentryAppenderTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(1, event.breadcrumbs.size) - assertEquals("this should be a breadcrumb", event.breadcrumbs[0].message) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(1, breadcrumbs.size) + assertEquals("this should be a breadcrumb", breadcrumbs[0].message) + } }, anyOrNull()) } } @@ -303,9 +309,11 @@ class SentryAppenderTest { await.untilAsserted { verify(fixture.transport).send(checkEvent { event -> - assertEquals(2, event.breadcrumbs.size) - assertEquals("this should be a breadcrumb", event.breadcrumbs[0].message) - assertEquals("this should not be sent as the event but be a breadcrumb", event.breadcrumbs[1].message) + assertNotNull(event.breadcrumbs) { breadcrumbs -> + assertEquals(2, breadcrumbs.size) + assertEquals("this should be a breadcrumb", breadcrumbs[0].message) + assertEquals("this should not be sent as the event but be a breadcrumb", breadcrumbs[1].message) + } }, anyOrNull()) } } diff --git a/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt b/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt index 3daaec2405..faa4aef821 100644 --- a/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt +++ b/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt @@ -133,7 +133,8 @@ class SentrySpringIntegrationTest { await.untilAsserted { verify(transport).send(checkEvent { event -> - assertThat(event.message.message).isEqualTo("event from logger") + assertThat(event.message).isNotNull() + assertThat(event.message!!.message).isEqualTo("event from logger") }, anyOrNull()) } } diff --git a/sentry/build.gradle.kts b/sentry/build.gradle.kts index 49f4f271ac..d6641e5d2a 100644 --- a/sentry/build.gradle.kts +++ b/sentry/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { errorprone(Config.CompileOnly.errorprone) errorproneJavac(Config.CompileOnly.errorProneJavac8) compileOnly(Config.CompileOnly.jetbrainsAnnotations) - errorprone(Config.CompileOnly.errorProneNullAway) +// errorprone(Config.CompileOnly.errorProneNullAway) // tests testImplementation(kotlin(Config.kotlinStdLib)) @@ -36,6 +36,7 @@ dependencies { testImplementation(Config.TestLibs.mockitoKotlin) testImplementation(Config.TestLibs.mockitoInline) testImplementation(Config.TestLibs.awaitility) + testImplementation(Config.TestLibs.jsonUnit) testImplementation(project(":sentry-test-support")) } @@ -83,8 +84,8 @@ buildConfig { val generateBuildConfig by tasks tasks.withType() { dependsOn(generateBuildConfig) - options.errorprone { - check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) - option("NullAway:AnnotatedPackages", "io.sentry.protocol") - } +// options.errorprone { +// check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) +// option("NullAway:AnnotatedPackages", "io.sentry") +// } } diff --git a/sentry/src/main/java/io/sentry/ISerializer.java b/sentry/src/main/java/io/sentry/ISerializer.java index cf80288e3a..1a27e0bc7d 100644 --- a/sentry/src/main/java/io/sentry/ISerializer.java +++ b/sentry/src/main/java/io/sentry/ISerializer.java @@ -1,23 +1,25 @@ package io.sentry; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public interface ISerializer { @Nullable T deserialize(@NotNull Reader reader, @NotNull Class clazz); - @Nullable SentryEnvelope deserializeEnvelope(@NotNull InputStream inputStream); + @Nullable + SentryEnvelope deserializeEnvelope(@NotNull InputStream inputStream); void serialize(@NotNull T entity, @NotNull Writer writer) throws IOException; - void serialize(@NotNull SentryEnvelope envelope, @NotNull OutputStream outputStream) throws Exception; + void serialize(@NotNull SentryEnvelope envelope, @NotNull OutputStream outputStream) + throws Exception; - @NotNull String serialize(@NotNull Map data) throws Exception; + @NotNull + String serialize(@NotNull Map data) throws Exception; } diff --git a/sentry/src/main/java/io/sentry/NoOpSerializer.java b/sentry/src/main/java/io/sentry/NoOpSerializer.java index 79eb8264b7..ad517e2e5f 100644 --- a/sentry/src/main/java/io/sentry/NoOpSerializer.java +++ b/sentry/src/main/java/io/sentry/NoOpSerializer.java @@ -6,7 +6,6 @@ import java.io.Reader; import java.io.Writer; import java.util.Map; - import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,7 +34,8 @@ private NoOpSerializer() {} public void serialize(@NotNull T entity, @NotNull Writer writer) throws IOException {} @Override - public void serialize(@NotNull SentryEnvelope envelope, @NotNull OutputStream outputStream) throws Exception {} + public void serialize(@NotNull SentryEnvelope envelope, @NotNull OutputStream outputStream) + throws Exception {} @Override public @NotNull String serialize(@NotNull Map data) throws Exception { diff --git a/sentry/src/main/java/io/sentry/SentryBaseEvent.java b/sentry/src/main/java/io/sentry/SentryBaseEvent.java index 6c79e82e72..a029070ffe 100644 --- a/sentry/src/main/java/io/sentry/SentryBaseEvent.java +++ b/sentry/src/main/java/io/sentry/SentryBaseEvent.java @@ -47,7 +47,7 @@ public abstract class SentryBaseEvent { * *

A map or list of tags for this event. Each tag must be less than 200 characters. */ - private Map tags; + private @Nullable Map tags; /** * The release version of the application. @@ -55,14 +55,14 @@ public abstract class SentryBaseEvent { *

**Release versions must be unique across all projects in your organization.** This value can * be the git SHA for the given project, or a product identifier with a semantic version. */ - private String release; + private @Nullable String release; /** * The environment name, such as `production` or `staging`. * *

```json { "environment": "production" } ``` */ - private String environment; + private @Nullable String environment; /** * Platform identifier of this event (defaults to "other"). @@ -88,7 +88,7 @@ public abstract class SentryBaseEvent { * *

This is supposed to be a hostname. */ - private String serverName; + private @Nullable String serverName; /** * Program's distribution identifier. @@ -99,17 +99,17 @@ public abstract class SentryBaseEvent { * an application. For example, the dist can be the build number of an XCode build or the version * code of an Android build. */ - private String dist; + private @Nullable String dist; /** List of breadcrumbs recorded before this event. */ - private List breadcrumbs; + private @Nullable List breadcrumbs; /** * Arbitrary extra information set by the user. * *

```json { "extra": { "my_key": 1, "some_other_value": "foo bar" } }``` */ - private Map extra; + private @Nullable Map extra; protected SentryBaseEvent(final @NotNull SentryId eventId) { this.eventId = eventId; @@ -181,11 +181,11 @@ public void setThrowable(final @Nullable Throwable throwable) { } @ApiStatus.Internal - public Map getTags() { + public @Nullable Map getTags() { return tags; } - public void setTags(Map tags) { + public void setTags(@Nullable Map tags) { this.tags = tags; } @@ -202,26 +202,26 @@ public void removeTag(@NotNull String key) { return null; } - public void setTag(String key, String value) { + public void setTag(final @NotNull String key, final @NotNull String value) { if (tags == null) { tags = new HashMap<>(); } tags.put(key, value); } - public String getRelease() { + public @Nullable String getRelease() { return release; } - public void setRelease(String release) { + public void setRelease(final @Nullable String release) { this.release = release; } - public String getEnvironment() { + public @Nullable String getEnvironment() { return environment; } - public void setEnvironment(String environment) { + public void setEnvironment(final @Nullable String environment) { this.environment = environment; } @@ -233,19 +233,19 @@ public void setPlatform(final @Nullable String platform) { this.platform = platform; } - public String getServerName() { + public @Nullable String getServerName() { return serverName; } - public void setServerName(String serverName) { + public void setServerName(final @Nullable String serverName) { this.serverName = serverName; } - public String getDist() { + public @Nullable String getDist() { return dist; } - public void setDist(String dist) { + public void setDist(final @Nullable String dist) { this.dist = dist; } @@ -257,15 +257,15 @@ public void setUser(final @Nullable User user) { this.user = user; } - public List getBreadcrumbs() { + public @Nullable List getBreadcrumbs() { return breadcrumbs; } - public void setBreadcrumbs(List breadcrumbs) { + public void setBreadcrumbs(final @Nullable List breadcrumbs) { this.breadcrumbs = breadcrumbs; } - public void addBreadcrumb(Breadcrumb breadcrumb) { + public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { if (breadcrumbs == null) { breadcrumbs = new ArrayList<>(); } @@ -276,18 +276,18 @@ Map getExtras() { return extra; } - public void setExtras(Map extra) { + public void setExtras(final @Nullable Map extra) { this.extra = extra; } - public void setExtra(String key, Object value) { + public void setExtra(final @NotNull String key, final @NotNull Object value) { if (extra == null) { extra = new HashMap<>(); } extra.put(key, value); } - public void removeExtra(@NotNull String key) { + public void removeExtra(final @NotNull String key) { if (extra != null) { extra.remove(key); } diff --git a/sentry/src/main/java/io/sentry/SentryEvent.java b/sentry/src/main/java/io/sentry/SentryEvent.java index f700dba141..4d439c9531 100644 --- a/sentry/src/main/java/io/sentry/SentryEvent.java +++ b/sentry/src/main/java/io/sentry/SentryEvent.java @@ -27,16 +27,16 @@ public final class SentryEvent extends SentryBaseEvent implements IUnknownProper * *

```json { "timestamp": "2011-05-02T17:41:36Z" } { "timestamp": 1304358096.0 } ``` */ - private final Date timestamp; + private final @NotNull Date timestamp; - private Message message; + private @Nullable Message message; /** Logger that created the event. */ - private String logger; + private @Nullable String logger; /** Threads that were active when the event occurred. */ - private SentryValues threads; + private @Nullable SentryValues threads; /** One or multiple chained (nested) exceptions. */ - private SentryValues exception; + private @Nullable SentryValues exception; /** * Severity level of the event. Defaults to `error`. * @@ -44,14 +44,14 @@ public final class SentryEvent extends SentryBaseEvent implements IUnknownProper * *

```json {"level": "warning"} ``` */ - private SentryLevel level; + private @Nullable SentryLevel level; /** * Transaction name of the event. * *

For example, in a web app, this might be the route name (`"/users//"` or * `UserView`), in a task queue it might be the function + module name. */ - private String transaction; + private @Nullable String transaction; /** * Manual fingerprint override. @@ -62,9 +62,9 @@ public final class SentryEvent extends SentryBaseEvent implements IUnknownProper * *

```json { "fingerprint": ["myrpc", "POST", "/foo.bar"] } */ - private List fingerprint; + private @Nullable List fingerprint; - private Map unknown; + private @Nullable Map unknown; /** * Name and versions of all installed modules/packages/dependencies in the current * environment/application. @@ -77,11 +77,11 @@ public final class SentryEvent extends SentryBaseEvent implements IUnknownProper *

This is primarily used for suggesting to enable certain SDK integrations from within the UI * and for making informed decisions on which frameworks to support in future development efforts. */ - private Map modules; + private @Nullable Map modules; /** Meta data for event processing and debugging. */ - private DebugMeta debugMeta; + private @Nullable DebugMeta debugMeta; - SentryEvent(SentryId eventId, final Date timestamp) { + SentryEvent(final @NotNull SentryId eventId, final @NotNull Date timestamp) { super(eventId); this.timestamp = timestamp; } @@ -101,7 +101,7 @@ public SentryEvent() { } @TestOnly - public SentryEvent(final Date timestamp) { + public SentryEvent(final @NotNull Date timestamp) { this(new SentryId(), timestamp); } @@ -110,23 +110,23 @@ public Date getTimestamp() { return (Date) timestamp.clone(); } - public Message getMessage() { + public @Nullable Message getMessage() { return message; } - public void setMessage(Message message) { + public void setMessage(final @Nullable Message message) { this.message = message; } - public String getLogger() { + public @Nullable String getLogger() { return logger; } - public void setLogger(String logger) { + public void setLogger(final @Nullable String logger) { this.logger = logger; } - public List getThreads() { + public @Nullable List getThreads() { if (threads != null) { return threads.getValues(); } else { @@ -146,57 +146,58 @@ public void setExceptions(List exception) { this.exception = new SentryValues<>(exception); } - public SentryLevel getLevel() { + public @Nullable SentryLevel getLevel() { return level; } - public void setLevel(SentryLevel level) { + public void setLevel(final @Nullable SentryLevel level) { this.level = level; } - public String getTransaction() { + public @Nullable String getTransaction() { return transaction; } - public void setTransaction(String transaction) { + public void setTransaction(final @Nullable String transaction) { this.transaction = transaction; } - public List getFingerprints() { + public @NotNull List getFingerprints() { return fingerprint; } - public void setFingerprints(List fingerprint) { + public void setFingerprints(final @NotNull List fingerprint) { this.fingerprint = fingerprint; } @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } @TestOnly - public Map getUnknown() { + public @Nullable Map getUnknown() { return unknown; } + @Nullable Map getModules() { return modules; } - public void setModules(Map modules) { + public void setModules(final @Nullable Map modules) { this.modules = modules; } - public void setModule(String key, String value) { + public void setModule(final @NotNull String key, final @NotNull String value) { if (modules == null) { modules = new HashMap<>(); } modules.put(key, value); } - public void removeModule(@NotNull String key) { + public void removeModule(final @NotNull String key) { if (modules != null) { modules.remove(key); } @@ -209,11 +210,11 @@ public void removeModule(@NotNull String key) { return null; } - public DebugMeta getDebugMeta() { + public @Nullable DebugMeta getDebugMeta() { return debugMeta; } - public void setDebugMeta(DebugMeta debugMeta) { + public void setDebugMeta(final @Nullable DebugMeta debugMeta) { this.debugMeta = debugMeta; } diff --git a/sentry/src/test/java/io/sentry/GsonSerializerTest.kt b/sentry/src/test/java/io/sentry/GsonSerializerTest.kt index 09ac3c52f1..9012edff72 100644 --- a/sentry/src/test/java/io/sentry/GsonSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/GsonSerializerTest.kt @@ -33,6 +33,9 @@ import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue +import net.javacrumbs.jsonunit.JsonMatchers.jsonEquals +import net.javacrumbs.jsonunit.core.Option +import org.junit.Assert.assertThat class GsonSerializerTest { @@ -85,13 +88,13 @@ class GsonSerializerTest { @Test fun `when serializing SentryEvent-SentryId object, it should become a event_id json without dashes`() { - val sentryEvent = generateEmptySentryEvent(null) + val sentryEvent = generateEmptySentryEvent() val actual = serializeToString(sentryEvent) val expected = "{\"event_id\":\"${sentryEvent.eventId}\",\"contexts\":{}}" - assertEquals(expected, actual) + assertJsonContains(actual, expected) } @Test @@ -162,9 +165,9 @@ class GsonSerializerTest { val actual = fixture.serializer.deserialize(StringReader(jsonEvent), SentryEvent::class.java) - assertEquals("test", (actual!!.unknown["string"] as JsonPrimitive).asString) - assertEquals(1, (actual.unknown["int"] as JsonPrimitive).asInt) - assertEquals(true, (actual.unknown["boolean"] as JsonPrimitive).asBoolean) + assertEquals("test", (actual!!.unknown!!["string"] as JsonPrimitive).asString) + assertEquals(1, (actual.unknown!!["int"] as JsonPrimitive).asInt) + assertEquals(true, (actual.unknown!!["boolean"] as JsonPrimitive).asBoolean) } @Test @@ -184,7 +187,7 @@ class GsonSerializerTest { val actual = fixture.serializer.deserialize(StringReader(jsonEvent), SentryEvent::class.java) - val hashMapActual = actual!!.unknown["object"] as JsonObject // gson creates it as JsonObject + val hashMapActual = actual!!.unknown!!["object"] as JsonObject // gson creates it as JsonObject assertEquals(true, hashMapActual.get("boolean").asBoolean) assertEquals(1, (hashMapActual.get("int")).asInt) @@ -192,7 +195,7 @@ class GsonSerializerTest { @Test fun `when serializing unknown field, it should become unknown as json format`() { - val sentryEvent = generateEmptySentryEvent(null) + val sentryEvent = generateEmptySentryEvent() sentryEvent.eventId = null val objects = hashMapOf() @@ -208,12 +211,12 @@ class GsonSerializerTest { val expected = "{\"unknown\":{\"object\":{\"boolean\":true,\"int\":1}},\"contexts\":{}}" - assertEquals(expected, actual) + assertJsonContains(actual, expected) } @Test fun `when serializing a TimeZone, it should become a timezone ID string`() { - val sentryEvent = generateEmptySentryEvent(null) + val sentryEvent = generateEmptySentryEvent() sentryEvent.eventId = null val device = Device() device.timezone = TimeZone.getTimeZone("Europe/Vienna") @@ -223,7 +226,7 @@ class GsonSerializerTest { val actual = serializeToString(sentryEvent) - assertEquals(expected, actual) + assertJsonContains(actual, expected) } @Test @@ -240,17 +243,19 @@ class GsonSerializerTest { @Test fun `when serializing a DeviceOrientation, it should become an orientation string`() { - val sentryEvent = generateEmptySentryEvent(null) + val sentryEvent = generateEmptySentryEvent() sentryEvent.eventId = null val device = Device() device.orientation = Device.DeviceOrientation.LANDSCAPE sentryEvent.contexts.setDevice(device) val expected = "{\"contexts\":{\"device\":{\"orientation\":\"landscape\"}}}" - val actual = serializeToString(sentryEvent) + assertJsonContains(actual, expected) + } - assertEquals(expected, actual) + private fun assertJsonContains(actual: String, expected: String) { + assertThat(actual, jsonEquals(expected).`when`(Option.IGNORING_EXTRA_FIELDS)) } @Test @@ -267,7 +272,7 @@ class GsonSerializerTest { @Test fun `when serializing a SentryLevel, it should become a sentry level string`() { - val sentryEvent = generateEmptySentryEvent(null) + val sentryEvent = generateEmptySentryEvent() sentryEvent.eventId = null sentryEvent.level = SentryLevel.DEBUG @@ -275,7 +280,7 @@ class GsonSerializerTest { val actual = serializeToString(sentryEvent) - assertEquals(expected, actual) + assertJsonContains(actual, expected) } @Test @@ -296,7 +301,11 @@ class GsonSerializerTest { val actual = fixture.serializer.deserialize(StringReader(jsonEvent), SentryEvent::class.java) - assertEquals(2, actual!!.breadcrumbs.size) + assertNotNull(actual) { event -> + assertNotNull(event.breadcrumbs) { + assertEquals(2, it.size) + } + } } @Test @@ -593,8 +602,8 @@ class GsonSerializerTest { verify(fixture.logger) .log(eq(SentryLevel.ERROR), - eq("Failed to create envelope item. Dropping it."), - any()) + eq("Failed to create envelope item. Dropping it."), + any()) } @Test @@ -657,7 +666,7 @@ class GsonSerializerTest { } } - private fun generateEmptySentryEvent(date: Date? = null): SentryEvent = + private fun generateEmptySentryEvent(date: Date = Date()): SentryEvent = SentryEvent(date) private fun createSessionMockData(): Session = diff --git a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt index e8355833dd..78c0ff87b0 100644 --- a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt @@ -79,7 +79,7 @@ class MainEventProcessorTest { var event = generateCrashedEvent(crashedThread) event = sut.process(event, null) - assertTrue(event.threads.any { it.isCrashed == true }) + assertTrue(event.threads!!.any { it.isCrashed == true }) } @Test @@ -93,7 +93,7 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed == true) + assertTrue(event.threads!!.first { t -> t.id == crashedThread.id }.isCrashed == true) } @Test @@ -107,7 +107,7 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed == true) + assertTrue(event.threads!!.first { t -> t.id == crashedThread.id }.isCrashed == true) } @Test @@ -152,7 +152,7 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads.first { t -> t.id == crashedThread.id }.isCrashed == true) + assertTrue(event.threads!!.first { t -> t.id == crashedThread.id }.isCrashed == true) } @Test @@ -182,7 +182,7 @@ class MainEventProcessorTest { var event = SentryEvent() event = sut.process(event, null) - assertEquals(1, event.threads.count()) + assertEquals(1, event.threads!!.count()) } @Test @@ -269,8 +269,8 @@ class MainEventProcessorTest { val sut = fixture.getSut(tags = mapOf("tag1" to "value1", "tag2" to "value2")) val event = SentryEvent() sut.process(event, null) - assertEquals("value1", event.tags["tag1"]) - assertEquals("value2", event.tags["tag2"]) + assertEquals("value1", event.tags!!["tag1"]) + assertEquals("value2", event.tags!!["tag2"]) } @Test @@ -279,8 +279,8 @@ class MainEventProcessorTest { val event = SentryEvent() event.setTag("tag2", "event-tag-value") sut.process(event, null) - assertEquals("value1", event.tags["tag1"]) - assertEquals("event-tag-value", event.tags["tag2"]) + assertEquals("value1", event.tags!!["tag1"]) + assertEquals("event-tag-value", event.tags!!["tag2"]) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 17e37708b6..e4c23aacf1 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -160,7 +160,7 @@ class SentryClientTest { sut.captureEvent(actual) verify(fixture.transport).send(check { val event = getEventFromData(it.items.first().data) - assertEquals("test", event.tags["test"]) + assertEquals("test", event.tags!!["test"]) }, anyOrNull()) verifyNoMoreInteractions(fixture.transport) } @@ -175,10 +175,12 @@ class SentryClientTest { val actual = SentryEvent() sut.captureEvent(actual) - assertEquals("test", actual.breadcrumbs.first().data["sentry:message"]) - assertEquals("SentryClient", actual.breadcrumbs.first().category) - assertEquals(SentryLevel.ERROR, actual.breadcrumbs.first().level) - assertEquals("BeforeSend callback failed.", actual.breadcrumbs.first().message) + assertNotNull(actual.breadcrumbs) { + assertEquals("test", it.first().data["sentry:message"]) + assertEquals("SentryClient", it.first().category) + assertEquals(SentryLevel.ERROR, it.first().level) + assertEquals("BeforeSend callback failed.", it.first().message) + } } @Test @@ -198,7 +200,7 @@ class SentryClientTest { val sut = fixture.getSut() val actual = "actual message" sut.captureMessage(actual, null) - assertEquals(actual, sentEvent!!.message.formatted) + assertEquals(actual, sentEvent!!.message!!.formatted) } @Test @@ -260,9 +262,9 @@ class SentryClientTest { val sut = fixture.getSut() sut.captureEvent(event, scope) - assertEquals("message", event.breadcrumbs[0].message) + assertEquals("message", event.breadcrumbs!![0].message) assertEquals("extra", event.extras["extra"]) - assertEquals("tags", event.tags["tags"]) + assertEquals("tags", event.tags!!["tags"]) assertEquals("fp", event.fingerprints[0]) assertNotNull(event.user) { assertEquals("id", it.id) @@ -291,9 +293,11 @@ class SentryClientTest { sut.captureEvent(event, scope) - assertSame(b1, event.breadcrumbs[0]) - assertSame(b2, event.breadcrumbs[1]) - assertSame(b3, event.breadcrumbs[2]) + assertNotNull(event.breadcrumbs) { + assertSame(b1, it[0]) + assertSame(b2, it[1]) + assertSame(b3, it[2]) + } } @Test @@ -307,16 +311,20 @@ class SentryClientTest { sut.captureEvent(event, scope) // breadcrumbs are appending - assertEquals("eventMessage", event.breadcrumbs[0].message) - assertEquals("message", event.breadcrumbs[1].message) + assertNotNull(event.breadcrumbs) { + assertEquals("eventMessage", it[0].message) + assertEquals("message", it[1].message) + } // extras are appending assertEquals("eventExtra", event.extras["eventExtra"]) assertEquals("extra", event.extras["extra"]) // tags are appending - assertEquals("eventTag", event.tags["eventTag"]) - assertEquals("tags", event.tags["tags"]) + assertNotNull(event.tags) { + assertEquals("eventTag", it["eventTag"]) + assertEquals("tags", it["tags"]) + } // fingerprint is replaced assertEquals("eventFp", event.fingerprints[0]) @@ -347,7 +355,9 @@ class SentryClientTest { assertEquals("eventExtra", event.extras["eventExtra"]) // tags are appending - assertEquals("eventTag", event.tags["eventTag"]) + assertNotNull(event.tags) { + assertEquals("eventTag", it["eventTag"]) + } } @Test @@ -848,7 +858,9 @@ class SentryClientTest { assertNotNull(it.request) { request -> assertEquals("/url", request.url) } - assertEquals("message", it.breadcrumbs.first().message) + assertNotNull(it.breadcrumbs) { breadcrumbs -> + assertEquals("message", breadcrumbs.first().message) + } assertEquals("b", it.getExtra("a")) } }, eq(null)) diff --git a/sentry/src/test/java/io/sentry/SentryEventTest.kt b/sentry/src/test/java/io/sentry/SentryEventTest.kt index 5c17c5966c..4b0dc7d4b2 100644 --- a/sentry/src/test/java/io/sentry/SentryEventTest.kt +++ b/sentry/src/test/java/io/sentry/SentryEventTest.kt @@ -10,6 +10,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotEquals +import kotlin.test.assertNotNull import kotlin.test.assertTrue class SentryEventTest { @@ -71,7 +72,9 @@ class SentryEventTest { fun `adds breadcrumb with string as a parameter`() { val event = SentryEvent() event.addBreadcrumb("breadcrumb") - assertEquals(1, event.breadcrumbs.filter { it.message == "breadcrumb" }.size) + assertNotNull(event.breadcrumbs) { + assertEquals(1, it.filter { it.message == "breadcrumb" }.size) + } } @Test From 65bdc228afad789e46477483c558011453b37877 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 11:17:49 +0200 Subject: [PATCH 19/40] Add nullability annotations. --- .../ApacheHttpClientTransportFactory.java | 2 +- .../boot/it/SentrySpringIntegrationTest.kt | 4 ++-- .../spring/SentrySpringIntegrationTest.kt | 4 ++-- sentry/api/sentry.api | 2 +- sentry/src/main/java/io/sentry/Hub.java | 2 +- sentry/src/main/java/io/sentry/IHub.java | 2 +- .../main/java/io/sentry/IScopeObserver.java | 14 ++++++----- .../main/java/io/sentry/ISentryClient.java | 2 +- .../java/io/sentry/ITransportFactory.java | 1 + sentry/src/main/java/io/sentry/NoOpHub.java | 10 ++++---- .../main/java/io/sentry/NoOpSentryClient.java | 2 +- .../main/java/io/sentry/NoOpTransaction.java | 2 +- .../java/io/sentry/NoOpTransportFactory.java | 2 +- sentry/src/main/java/io/sentry/Scope.java | 5 +++- sentry/src/main/java/io/sentry/Sentry.java | 4 ++-- .../main/java/io/sentry/SentryBaseEvent.java | 1 + .../src/main/java/io/sentry/SentryEvent.java | 5 ++-- .../java/io/sentry/cache/EnvelopeCache.java | 19 +++++++++++++-- .../sentry/exception/InvalidDsnException.java | 15 +++++++----- .../java/io/sentry/transport/RateLimiter.java | 5 +++- .../java/io/sentry/MainEventProcessorTest.kt | 10 ++++---- .../test/java/io/sentry/SentryClientTest.kt | 24 +++++++++++++------ .../java/io/sentry/cache/EnvelopeCacheTest.kt | 2 +- 23 files changed, 90 insertions(+), 49 deletions(-) diff --git a/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransportFactory.java b/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransportFactory.java index e2e62f263d..869c84949d 100644 --- a/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransportFactory.java +++ b/sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransportFactory.java @@ -32,7 +32,7 @@ public ApacheHttpClientTransportFactory(final @NotNull TimeValue connectionTimeT } @Override - public ITransport create( + public @NotNull ITransport create( final @NotNull SentryOptions options, final @NotNull RequestDetails requestDetails) { Objects.requireNonNull(options, "options is required"); Objects.requireNonNull(requestDetails, "requestDetails is required"); diff --git a/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt b/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt index faa4aef821..e84f5065f7 100644 --- a/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt +++ b/sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt @@ -116,8 +116,8 @@ class SentrySpringIntegrationTest { await.untilAsserted { verify(transport).send(checkEvent { event -> - assertThat(event.exceptions).isNotEmpty - val ex = event.exceptions.first() + assertThat(event.exceptions).isNotNull().isNotEmpty + val ex = event.exceptions!!.first() assertThat(ex.value).isEqualTo("something went wrong") assertThat(ex.mechanism).isNotNull() assertThat(ex.mechanism!!.isHandled).isFalse() diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt index df1544d37b..fc7119a25a 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt @@ -124,8 +124,8 @@ class SentrySpringIntegrationTest { await.untilAsserted { verify(transport).send(checkEvent { event -> - assertThat(event.exceptions).isNotEmpty - val ex = event.exceptions.first() + assertThat(event.exceptions).isNotNull().isNotEmpty + val ex = event.exceptions!!.first() assertThat(ex.value).isEqualTo("something went wrong") assertThat(ex.mechanism).isNotNull() assertThat(ex.mechanism!!.isHandled).isFalse() diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index a65bea6266..13a015ef59 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1220,7 +1220,7 @@ public final class io/sentry/cache/EnvelopeCache : io/sentry/cache/IEnvelopeCach public static final field PREFIX_CURRENT_SESSION_FILE Ljava/lang/String; public static final field SUFFIX_ENVELOPE_FILE Ljava/lang/String; protected static final field UTF_8 Ljava/nio/charset/Charset; - public fun (Lio/sentry/SentryOptions;)V + public static fun create (Lio/sentry/SentryOptions;)Lio/sentry/cache/IEnvelopeCache; public fun discard (Lio/sentry/SentryEnvelope;)V public fun iterator ()Ljava/util/Iterator; public fun store (Lio/sentry/SentryEnvelope;Ljava/lang/Object;)V diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index eab821fc7c..ee89fbad44 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -653,7 +653,7 @@ public void setSpanContext( SpanContext getSpanContext(final @NotNull Throwable throwable) { Objects.requireNonNull(throwable, "throwable is required"); final Pair span = this.throwableToSpan.get(throwable); - if (span != null) { + if (span != null && span.getFirst() != null) { return span.getFirst().getSpanContext(); } return null; diff --git a/sentry/src/main/java/io/sentry/IHub.java b/sentry/src/main/java/io/sentry/IHub.java index 5f3ca8ccb0..0f88661eba 100644 --- a/sentry/src/main/java/io/sentry/IHub.java +++ b/sentry/src/main/java/io/sentry/IHub.java @@ -220,7 +220,7 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * * @return last SentryId */ - @Nullable + @NotNull SentryId getLastEventId(); /** Pushes a new scope while inheriting the current scope's data. */ diff --git a/sentry/src/main/java/io/sentry/IScopeObserver.java b/sentry/src/main/java/io/sentry/IScopeObserver.java index 5290079730..baf06d71c5 100644 --- a/sentry/src/main/java/io/sentry/IScopeObserver.java +++ b/sentry/src/main/java/io/sentry/IScopeObserver.java @@ -1,18 +1,20 @@ package io.sentry; import io.sentry.protocol.User; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** Observer for the sync. of Scopes across SDKs */ public interface IScopeObserver { - void setUser(User user); + void setUser(@Nullable User user); - void addBreadcrumb(Breadcrumb crumb); + void addBreadcrumb(@NotNull Breadcrumb crumb); - void setTag(String key, String value); + void setTag(@NotNull String key, @Nullable String value); - void removeTag(String key); + void removeTag(@NotNull String key); - void setExtra(String key, String value); + void setExtra(@NotNull String key, @NotNull String value); - void removeExtra(String key); + void removeExtra(@NotNull String key); } diff --git a/sentry/src/main/java/io/sentry/ISentryClient.java b/sentry/src/main/java/io/sentry/ISentryClient.java index 9337dbd0ce..8ecce13192 100644 --- a/sentry/src/main/java/io/sentry/ISentryClient.java +++ b/sentry/src/main/java/io/sentry/ISentryClient.java @@ -198,7 +198,7 @@ default SentryId captureEnvelope(SentryEnvelope envelope) { */ @NotNull SentryId captureTransaction( - @NotNull SentryTransaction transaction, @NotNull Scope scope, @Nullable Object hint); + @NotNull SentryTransaction transaction, @Nullable Scope scope, @Nullable Object hint); /** * Captures a transaction without scope nor hint. diff --git a/sentry/src/main/java/io/sentry/ITransportFactory.java b/sentry/src/main/java/io/sentry/ITransportFactory.java index 46de4e314f..fddc7e9eb8 100644 --- a/sentry/src/main/java/io/sentry/ITransportFactory.java +++ b/sentry/src/main/java/io/sentry/ITransportFactory.java @@ -13,6 +13,7 @@ public interface ITransportFactory { * the transport * @return the transport */ + @NotNull ITransport create( final @NotNull SentryOptions options, final @NotNull RequestDetails requestDetails); } diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index ac5af855ca..2ef71d60d7 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -61,13 +61,13 @@ public void close() {} public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Object hint) {} @Override - public void setLevel(SentryLevel level) {} + public void setLevel(@Nullable SentryLevel level) {} @Override - public void setTransaction(String transaction) {} + public void setTransaction(@Nullable String transaction) {} @Override - public void setUser(User user) {} + public void setUser(@Nullable User user) {} @Override public void setFingerprint(@NotNull List fingerprint) {} @@ -88,7 +88,7 @@ public void setExtra(@NotNull String key, @NotNull String value) {} public void removeExtra(@NotNull String key) {} @Override - public SentryId getLastEventId() { + public @NotNull SentryId getLastEventId() { return SentryId.EMPTY_ID; } @@ -129,7 +129,7 @@ public void flush(long timeoutMillis) {} @Override public @NotNull ITransaction startTransaction( @NotNull TransactionContext transactionContexts, - CustomSamplingContext customSamplingContext, + @Nullable CustomSamplingContext customSamplingContext, boolean bindToScope) { return NoOpTransaction.getInstance(); } diff --git a/sentry/src/main/java/io/sentry/NoOpSentryClient.java b/sentry/src/main/java/io/sentry/NoOpSentryClient.java index 14eccc68c8..5a57893e4c 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryClient.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryClient.java @@ -44,7 +44,7 @@ public SentryId captureEnvelope(SentryEnvelope envelope, @Nullable Object hint) @Override public @NotNull SentryId captureTransaction( - @NotNull SentryTransaction transaction, @NotNull Scope scope, @Nullable Object hint) { + @NotNull SentryTransaction transaction, @Nullable Scope scope, @Nullable Object hint) { return SentryId.EMPTY_ID; } } diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index 7afe6e0b8e..e43059e18c 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -24,7 +24,7 @@ public void setName(@NotNull String name) {} @Override public @NotNull String getName() { - return null; + return ""; } @Override diff --git a/sentry/src/main/java/io/sentry/NoOpTransportFactory.java b/sentry/src/main/java/io/sentry/NoOpTransportFactory.java index d87ba212d1..e455d2354e 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransportFactory.java +++ b/sentry/src/main/java/io/sentry/NoOpTransportFactory.java @@ -17,7 +17,7 @@ public static NoOpTransportFactory getInstance() { private NoOpTransportFactory() {} @Override - public ITransport create( + public @NotNull ITransport create( final @NotNull SentryOptions options, final @NotNull RequestDetails requestDetails) { return NoOpTransport.getInstance(); } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 1f17805947..f7aeef766a 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -112,6 +112,7 @@ public void setLevel(final @Nullable SentryLevel level) { * * @param transaction the transaction */ + // todo: make nullable, unset transaction if null public void setTransaction(final @NotNull String transaction) { final ITransaction tx = this.transaction; if (tx != null) { @@ -245,7 +246,9 @@ Queue getBreadcrumbs() { "The BeforeBreadcrumbCallback callback threw an exception. It will be added as breadcrumb and continue.", e); - breadcrumb.setData("sentry:message", e.getMessage()); + if (e.getMessage() != null) { + breadcrumb.setData("sentry:message", e.getMessage()); + } } return breadcrumb; } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 7bf097a57a..8f8b31ce1c 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -217,7 +217,7 @@ private static boolean initConfigurations(final @NotNull SentryOptions options) final File outboxDir = new File(options.getOutboxPath()); outboxDir.mkdirs(); - options.setEnvelopeDiskCache(new EnvelopeCache(options)); + options.setEnvelopeDiskCache(EnvelopeCache.create(options)); } else { logger.log(SentryLevel.INFO, "No outbox dir path is defined in options."); } @@ -639,7 +639,7 @@ public static void endSession() { */ public static @NotNull ITransaction startTransaction( final @NotNull TransactionContext transactionContexts, - final @NotNull CustomSamplingContext customSamplingContext, + final @Nullable CustomSamplingContext customSamplingContext, final boolean bindToScope) { return getCurrentHub() .startTransaction(transactionContexts, customSamplingContext, bindToScope); diff --git a/sentry/src/main/java/io/sentry/SentryBaseEvent.java b/sentry/src/main/java/io/sentry/SentryBaseEvent.java index a029070ffe..d5741a9522 100644 --- a/sentry/src/main/java/io/sentry/SentryBaseEvent.java +++ b/sentry/src/main/java/io/sentry/SentryBaseEvent.java @@ -272,6 +272,7 @@ public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { breadcrumbs.add(breadcrumb); } + @Nullable Map getExtras() { return extra; } diff --git a/sentry/src/main/java/io/sentry/SentryEvent.java b/sentry/src/main/java/io/sentry/SentryEvent.java index 4d439c9531..0b3dea90aa 100644 --- a/sentry/src/main/java/io/sentry/SentryEvent.java +++ b/sentry/src/main/java/io/sentry/SentryEvent.java @@ -138,6 +138,7 @@ public void setThreads(List threads) { this.threads = new SentryValues<>(threads); } + @Nullable public List getExceptions() { return exception == null ? null : exception.getValues(); } @@ -162,11 +163,11 @@ public void setTransaction(final @Nullable String transaction) { this.transaction = transaction; } - public @NotNull List getFingerprints() { + public @Nullable List getFingerprints() { return fingerprint; } - public void setFingerprints(final @NotNull List fingerprint) { + public void setFingerprints(final @Nullable List fingerprint) { this.fingerprint = fingerprint; } diff --git a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java index 48e862c486..caacd79f1f 100644 --- a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java @@ -15,6 +15,7 @@ import io.sentry.Session; import io.sentry.hints.SessionEnd; import io.sentry.hints.SessionStart; +import io.sentry.transport.NoOpEnvelopeCache; import io.sentry.util.Objects; import java.io.BufferedInputStream; import java.io.BufferedReader; @@ -54,8 +55,22 @@ public final class EnvelopeCache extends CacheStrategy implements IEnvelopeCache private final @NotNull Map fileNameMap = new WeakHashMap<>(); - public EnvelopeCache(final @NotNull SentryOptions options) { - super(options, options.getCacheDirPath(), options.getCacheDirSize()); + public static IEnvelopeCache create(final @NotNull SentryOptions options) { + final String cacheDirPath = options.getCacheDirPath(); + final int cacheDirSize = options.getCacheDirSize(); + if (cacheDirPath == null) { + options.getLogger().log(WARNING, "cacheDirPath is null, returning NoOpEnvelopeCache"); + return NoOpEnvelopeCache.getInstance(); + } else { + return new EnvelopeCache(options, cacheDirPath, cacheDirSize); + } + } + + private EnvelopeCache( + final @NotNull SentryOptions options, + final @NotNull String cacheDirPath, + final @NotNull int cacheDirSize) { + super(options, cacheDirPath, cacheDirSize); } @Override diff --git a/sentry/src/main/java/io/sentry/exception/InvalidDsnException.java b/sentry/src/main/java/io/sentry/exception/InvalidDsnException.java index 5d045603e3..1597192b92 100644 --- a/sentry/src/main/java/io/sentry/exception/InvalidDsnException.java +++ b/sentry/src/main/java/io/sentry/exception/InvalidDsnException.java @@ -1,29 +1,32 @@ package io.sentry.exception; +import org.jetbrains.annotations.Nullable; + public final class InvalidDsnException extends RuntimeException { private static final long serialVersionUID = 412945154259913013L; - private final String dsn; + private final @Nullable String dsn; - public InvalidDsnException(String dsn) { + public InvalidDsnException(final @Nullable String dsn) { this.dsn = dsn; } - public InvalidDsnException(String dsn, String message) { + public InvalidDsnException(final @Nullable String dsn, final @Nullable String message) { super(message); this.dsn = dsn; } - public InvalidDsnException(String dsn, String message, Throwable cause) { + public InvalidDsnException( + final @Nullable String dsn, final @Nullable String message, final @Nullable Throwable cause) { super(message, cause); this.dsn = dsn; } - public InvalidDsnException(String dsn, Throwable cause) { + public InvalidDsnException(final @Nullable String dsn, final @Nullable Throwable cause) { super(cause); this.dsn = dsn; } - public String getDsn() { + public @Nullable String getDsn() { return dsn; } } diff --git a/sentry/src/main/java/io/sentry/transport/RateLimiter.java b/sentry/src/main/java/io/sentry/transport/RateLimiter.java index 5ca8849847..428eebd71f 100644 --- a/sentry/src/main/java/io/sentry/transport/RateLimiter.java +++ b/sentry/src/main/java/io/sentry/transport/RateLimiter.java @@ -203,7 +203,10 @@ public void updateRetryAfterLimits( for (final String catItem : categories) { DataCategory dataCategory = DataCategory.Unknown; try { - dataCategory = DataCategory.valueOf(StringUtils.capitalize(catItem)); + final String catItemCapitalized = StringUtils.capitalize(catItem); + if (catItemCapitalized != null) { + dataCategory = DataCategory.valueOf(catItemCapitalized); + } } catch (IllegalArgumentException e) { logger.log(INFO, e, "Unknown category: %s", catItem); } diff --git a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt index 78c0ff87b0..5190fd8b20 100644 --- a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt @@ -62,13 +62,15 @@ class MainEventProcessorTest { var event = generateCrashedEvent(crashedThread) event = sut.process(event, null) - assertSame(crashedThread.id, event.exceptions.first().threadId) + assertNotNull(event.exceptions) { + assertSame(crashedThread.id, it.first().threadId) + assertNotNull(it.first().mechanism) { + assertFalse(it.isHandled!!) + } + } assertNotNull(event.threads) { assertTrue(it.first { t -> t.id == crashedThread.id }.isCrashed == true) } - assertNotNull(event.exceptions.first().mechanism) { - assertFalse(it.isHandled!!) - } } @Test diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index e4c23aacf1..93dfce9923 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -263,9 +263,13 @@ class SentryClientTest { sut.captureEvent(event, scope) assertEquals("message", event.breadcrumbs!![0].message) - assertEquals("extra", event.extras["extra"]) + assertNotNull(event.extras) { + assertEquals("extra", it["extra"]) + } assertEquals("tags", event.tags!!["tags"]) - assertEquals("fp", event.fingerprints[0]) + assertNotNull(event.fingerprints) { + assertEquals("fp", it[0]) + } assertNotNull(event.user) { assertEquals("id", it.id) } @@ -317,8 +321,10 @@ class SentryClientTest { } // extras are appending - assertEquals("eventExtra", event.extras["eventExtra"]) - assertEquals("extra", event.extras["extra"]) + assertNotNull(event.extras) { + assertEquals("eventExtra", it["eventExtra"]) + assertEquals("extra", it["extra"]) + } // tags are appending assertNotNull(event.tags) { @@ -327,8 +333,10 @@ class SentryClientTest { } // fingerprint is replaced - assertEquals("eventFp", event.fingerprints[0]) - assertEquals(1, event.fingerprints.size) + assertNotNull(event.fingerprints) { + assertEquals("eventFp", it[0]) + assertEquals(1, it.size) + } assertEquals("eventTransaction", event.transaction) @@ -352,7 +360,9 @@ class SentryClientTest { sut.captureEvent(event, scope) // extras are appending - assertEquals("eventExtra", event.extras["eventExtra"]) + assertNotNull(event.extras) { + assertEquals("eventExtra", it["eventExtra"]) + } // tags are appending assertNotNull(event.tags) { diff --git a/sentry/src/test/java/io/sentry/cache/EnvelopeCacheTest.kt b/sentry/src/test/java/io/sentry/cache/EnvelopeCacheTest.kt index 6ad1c0bac4..e0b73196dd 100644 --- a/sentry/src/test/java/io/sentry/cache/EnvelopeCacheTest.kt +++ b/sentry/src/test/java/io/sentry/cache/EnvelopeCacheTest.kt @@ -44,7 +44,7 @@ class EnvelopeCacheTest { options.setSerializer(serializer) options.setDebug(true) - return EnvelopeCache(options) + return EnvelopeCache.create(options) } } From dee57729a2b5c1695f445696ebdbe548808d7021 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 11:35:53 +0200 Subject: [PATCH 20/40] Add nullability annotations to logger classes. --- .../io/sentry/android/core/AndroidLogger.java | 13 ++++++++++--- sentry/src/main/java/io/sentry/ILogger.java | 6 +++++- sentry/src/main/java/io/sentry/NoOpLogger.java | 12 +++++++++--- .../src/main/java/io/sentry/SystemOutLogger.java | 16 +++++++++++++--- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java index a81f73cb4d..71fcc54460 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java @@ -20,12 +20,19 @@ public void log( @SuppressWarnings("AnnotateFormatMethod") @Override - public void log(SentryLevel level, Throwable throwable, String message, Object... args) { + public void log( + final @Nullable SentryLevel level, + final @Nullable Throwable throwable, + final @Nullable String message, + final @Nullable Object... args) { log(level, String.format(message, args), throwable); } @Override - public void log(SentryLevel level, String message, Throwable throwable) { + public void log( + final @Nullable SentryLevel level, + final @Nullable String message, + final @Nullable Throwable throwable) { switch (level) { case INFO: @@ -52,7 +59,7 @@ public boolean isEnabled(@Nullable SentryLevel level) { return true; } - private int toLogcatLevel(SentryLevel sentryLevel) { + private int toLogcatLevel(final @Nullable SentryLevel sentryLevel) { switch (sentryLevel) { case INFO: return Log.INFO; diff --git a/sentry/src/main/java/io/sentry/ILogger.java b/sentry/src/main/java/io/sentry/ILogger.java index e5a009854b..373905cbb0 100644 --- a/sentry/src/main/java/io/sentry/ILogger.java +++ b/sentry/src/main/java/io/sentry/ILogger.java @@ -31,7 +31,11 @@ public interface ILogger { * @param message The message. * @param args the formatting arguments */ - void log(SentryLevel level, Throwable throwable, String message, Object... args); + void log( + @Nullable SentryLevel level, + @Nullable Throwable throwable, + @Nullable String message, + @Nullable Object... args); /** * Whether this logger is enabled for the specified SentryLevel. diff --git a/sentry/src/main/java/io/sentry/NoOpLogger.java b/sentry/src/main/java/io/sentry/NoOpLogger.java index ae832b1d2a..20262abb67 100644 --- a/sentry/src/main/java/io/sentry/NoOpLogger.java +++ b/sentry/src/main/java/io/sentry/NoOpLogger.java @@ -14,13 +14,19 @@ public static NoOpLogger getInstance() { private NoOpLogger() {} @Override - public void log(SentryLevel level, String message, Object... args) {} + public void log( + @Nullable SentryLevel level, @Nullable String message, @Nullable Object... args) {} @Override - public void log(SentryLevel level, String message, Throwable throwable) {} + public void log( + @Nullable SentryLevel level, @Nullable String message, @Nullable Throwable throwable) {} @Override - public void log(SentryLevel level, Throwable throwable, String message, Object... args) {} + public void log( + @Nullable SentryLevel level, + @Nullable Throwable throwable, + @Nullable String message, + @Nullable Object... args) {} @Override public boolean isEnabled(@Nullable SentryLevel level) { diff --git a/sentry/src/main/java/io/sentry/SystemOutLogger.java b/sentry/src/main/java/io/sentry/SystemOutLogger.java index 38dc42b82a..5edcbde1ea 100644 --- a/sentry/src/main/java/io/sentry/SystemOutLogger.java +++ b/sentry/src/main/java/io/sentry/SystemOutLogger.java @@ -17,7 +17,10 @@ public final class SystemOutLogger implements ILogger { */ @SuppressWarnings("AnnotateFormatMethod") @Override - public void log(SentryLevel level, String message, Object... args) { + public void log( + final @Nullable SentryLevel level, + final @Nullable String message, + final @Nullable Object... args) { System.out.println(String.format("%s: %s", level, String.format(message, args))); } @@ -30,7 +33,10 @@ public void log(SentryLevel level, String message, Object... args) { */ @SuppressWarnings("AnnotateFormatMethod") @Override - public void log(SentryLevel level, String message, Throwable throwable) { + public void log( + final @Nullable SentryLevel level, + final @Nullable String message, + final @Nullable Throwable throwable) { if (throwable == null) { this.log(level, message); } else { @@ -51,7 +57,11 @@ public void log(SentryLevel level, String message, Throwable throwable) { */ @SuppressWarnings("AnnotateFormatMethod") @Override - public void log(SentryLevel level, Throwable throwable, String message, Object... args) { + public void log( + final @Nullable SentryLevel level, + final @Nullable Throwable throwable, + final @Nullable String message, + final @Nullable Object... args) { if (throwable == null) { this.log(level, message, args); } else { From 69726a0568f6b9e815cfefe6b3341e978c400a23 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 12:40:40 +0200 Subject: [PATCH 21/40] Add nullability annotations. --- .../core/AndroidOptionsInitializerTest.kt | 4 +++- sentry/src/main/java/io/sentry/Hub.java | 5 +++- .../io/sentry/ISentryExecutorService.java | 4 +++- .../io/sentry/NoOpSentryExecutorService.java | 23 +++++++++++++++++++ .../java/io/sentry/SentryExecutorService.java | 2 +- .../main/java/io/sentry/SentryOptions.java | 8 +++---- sentry/src/main/java/io/sentry/Session.java | 8 +++---- .../main/java/io/sentry/SessionAdapter.java | 4 +++- 8 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/NoOpSentryExecutorService.java 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 338927bf3d..68c94d2a51 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 @@ -141,7 +141,9 @@ class AndroidOptionsInitializerTest { AndroidOptionsInitializer.init(sentryOptions, mockContext) - assertTrue(sentryOptions.distinctId.isNotEmpty()) + assertNotNull(sentryOptions.distinctId) { + assertTrue(it.isNotEmpty()) + } val installation = File(context.filesDir, Installation.INSTALLATION) installation.deleteOnExit() diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index ee89fbad44..82b3dddba6 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -260,7 +260,10 @@ public void close() { ((Closeable) integration).close(); } } - options.getExecutorService().close(options.getShutdownTimeout()); + final ISentryExecutorService executorService = options.getExecutorService(); + if (executorService != null) { + executorService.close(options.getShutdownTimeout()); + } // Close the top-most client final StackItem item = stack.peek(); diff --git a/sentry/src/main/java/io/sentry/ISentryExecutorService.java b/sentry/src/main/java/io/sentry/ISentryExecutorService.java index 1f54c388b9..dad1f1d1b8 100644 --- a/sentry/src/main/java/io/sentry/ISentryExecutorService.java +++ b/sentry/src/main/java/io/sentry/ISentryExecutorService.java @@ -1,6 +1,7 @@ package io.sentry; import java.util.concurrent.Future; +import org.jetbrains.annotations.NotNull; /** Sentry Executor Service that sends cached events and envelopes on App. start. */ interface ISentryExecutorService { @@ -11,7 +12,8 @@ interface ISentryExecutorService { * @param runnable the Runnable * @return a Future of the Runnable */ - Future submit(Runnable runnable); + @NotNull + Future submit(final @NotNull Runnable runnable); /** * Closes the ThreadExecutor and awaits for the timeout diff --git a/sentry/src/main/java/io/sentry/NoOpSentryExecutorService.java b/sentry/src/main/java/io/sentry/NoOpSentryExecutorService.java new file mode 100644 index 0000000000..e8dec3ca1f --- /dev/null +++ b/sentry/src/main/java/io/sentry/NoOpSentryExecutorService.java @@ -0,0 +1,23 @@ +package io.sentry; + +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import org.jetbrains.annotations.NotNull; + +final class NoOpSentryExecutorService implements ISentryExecutorService { + private static final NoOpSentryExecutorService instance = new NoOpSentryExecutorService(); + + private NoOpSentryExecutorService() {} + + public static ISentryExecutorService getInstance() { + return instance; + } + + @Override + public @NotNull Future submit(final @NotNull Runnable runnable) { + return new FutureTask<>(() -> null); + } + + @Override + public void close(long timeoutMillis) {} +} diff --git a/sentry/src/main/java/io/sentry/SentryExecutorService.java b/sentry/src/main/java/io/sentry/SentryExecutorService.java index d115988d8e..dd2faa29ab 100644 --- a/sentry/src/main/java/io/sentry/SentryExecutorService.java +++ b/sentry/src/main/java/io/sentry/SentryExecutorService.java @@ -21,7 +21,7 @@ final class SentryExecutorService implements ISentryExecutorService { } @Override - public Future submit(final @NotNull Runnable runnable) { + public @NotNull Future submit(final @NotNull Runnable runnable) { return executorService.submit(runnable); } diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 1c1f424507..3b79712bee 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -206,7 +206,7 @@ public class SentryOptions { private long sessionTrackingIntervalMillis = 30000; // 30s /** The distinct Id (generated Guid) used for session tracking */ - private String distinctId; + private @Nullable String distinctId; /** The server name used in the Sentry messages. */ private @Nullable String serverName; @@ -220,7 +220,7 @@ public class SentryOptions { private @Nullable Boolean enableUncaughtExceptionHandler = true; /** Sentry Executor Service that sends cached events and envelopes on App. start. */ - private @NotNull ISentryExecutorService executorService; + private @NotNull ISentryExecutorService executorService = NoOpSentryExecutorService.getInstance(); /** connection timeout in milliseconds. */ private int connectionTimeoutMillis = 5000; @@ -960,7 +960,7 @@ public void setSessionTrackingIntervalMillis(long sessionTrackingIntervalMillis) * @return the distinct Id */ @ApiStatus.Internal - public String getDistinctId() { + public @Nullable String getDistinctId() { return distinctId; } @@ -970,7 +970,7 @@ public String getDistinctId() { * @param distinctId the distinct Id */ @ApiStatus.Internal - public void setDistinctId(String distinctId) { + public void setDistinctId(final @Nullable String distinctId) { this.distinctId = distinctId; } diff --git a/sentry/src/main/java/io/sentry/Session.java b/sentry/src/main/java/io/sentry/Session.java index 06710e1787..10785b74c8 100644 --- a/sentry/src/main/java/io/sentry/Session.java +++ b/sentry/src/main/java/io/sentry/Session.java @@ -55,7 +55,7 @@ public enum State { private final @Nullable String environment; /** the App's release */ - private final @NotNull String release; + private final @Nullable String release; /** The session lock, ops should be atomic */ private final @NotNull Object sessionLock = new Object(); @@ -73,7 +73,7 @@ public Session( final @Nullable String ipAddress, final @Nullable String userAgent, final @Nullable String environment, - final @NotNull String release) { + final @Nullable String release) { this.status = status; this.started = started; this.timestamp = timestamp; @@ -93,7 +93,7 @@ public Session( @Nullable String distinctId, final @Nullable User user, final @Nullable String environment, - final @NotNull String release) { + final @Nullable String release) { this( State.Ok, DateUtils.getCurrentDateTime(), @@ -138,7 +138,7 @@ public Session( return environment; } - public @NotNull String getRelease() { + public @Nullable String getRelease() { return release; } diff --git a/sentry/src/main/java/io/sentry/SessionAdapter.java b/sentry/src/main/java/io/sentry/SessionAdapter.java index d050c133fd..3233ef0415 100644 --- a/sentry/src/main/java/io/sentry/SessionAdapter.java +++ b/sentry/src/main/java/io/sentry/SessionAdapter.java @@ -73,7 +73,9 @@ public void write(JsonWriter writer, Session value) throws IOException { boolean hasInitAttrs = false; hasInitAttrs = initAttrs(writer, hasInitAttrs); - writer.name("release").value(value.getRelease()); + if (value.getRelease() != null) { + writer.name("release").value(value.getRelease()); + } if (value.getEnvironment() != null) { hasInitAttrs = initAttrs(writer, hasInitAttrs); From d6fcfef90b60f5fb88fd22ebefcad79cc2c3e7ab Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 13:27:01 +0200 Subject: [PATCH 22/40] Add nullability annotations. --- .../java/io/sentry/CircularFifoQueue.java | 27 ++++++++++--------- sentry/src/main/java/io/sentry/Dsn.java | 20 +++++++------- .../main/java/io/sentry/ISentryClient.java | 5 ++-- .../main/java/io/sentry/NoOpSentryClient.java | 2 +- sentry/src/main/java/io/sentry/Sentry.java | 4 ++- .../src/main/java/io/sentry/SentryClient.java | 10 ++++--- .../SentryEnvelopeItemHeaderAdapter.java | 8 ++++-- .../io/sentry/ShutdownHookIntegration.java | 2 +- .../UnknownPropertiesTypeAdapterFactory.java | 5 ++-- .../io/sentry/ShutdownHookIntegrationTest.kt | 7 +++-- 10 files changed, 54 insertions(+), 36 deletions(-) diff --git a/sentry/src/main/java/io/sentry/CircularFifoQueue.java b/sentry/src/main/java/io/sentry/CircularFifoQueue.java index 60a1df35a3..87c126b508 100644 --- a/sentry/src/main/java/io/sentry/CircularFifoQueue.java +++ b/sentry/src/main/java/io/sentry/CircularFifoQueue.java @@ -10,6 +10,8 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Queue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * CircularFifoQueue is a first-in first-out queue with a fixed size that replaces its oldest @@ -34,7 +36,7 @@ final class CircularFifoQueue extends AbstractCollection implements Queue< private static final long serialVersionUID = -8423413834657610406L; /** Underlying storage array. */ - private transient E[] elements; + private transient @NotNull E[] elements; /** Array index of first (oldest) queue element. */ private transient int start = 0; @@ -80,7 +82,7 @@ public CircularFifoQueue() { * @param coll the collection to copy into the queue, may not be null * @throws NullPointerException if the collection is null */ - public CircularFifoQueue(final Collection coll) { + public CircularFifoQueue(final @NotNull Collection coll) { this(coll.size()); addAll(coll); } @@ -92,7 +94,7 @@ public CircularFifoQueue(final Collection coll) { * @param out the output stream * @throws IOException if an I/O error occurs while writing to the output stream */ - private void writeObject(final ObjectOutputStream out) throws IOException { + private void writeObject(final @NotNull ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(size()); for (final E e : this) { @@ -108,7 +110,8 @@ private void writeObject(final ObjectOutputStream out) throws IOException { * @throws ClassNotFoundException if the class of a serialized object can not be found */ @SuppressWarnings("unchecked") - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + private void readObject(final @NotNull ObjectInputStream in) + throws IOException, ClassNotFoundException { in.defaultReadObject(); elements = (E[]) new Object[maxElements]; final int size = in.readInt(); @@ -202,7 +205,7 @@ public void clear() { * @throws NullPointerException if the given element is null */ @Override - public boolean add(final E element) { + public boolean add(final @NotNull E element) { if (null == element) { throw new NullPointerException("Attempted to add null object to queue"); } @@ -231,7 +234,7 @@ public boolean add(final E element) { * @return the element at position {@code index} * @throws NoSuchElementException if the requested position is outside the range [0, size) */ - public E get(final int index) { + public @NotNull E get(final int index) { final int sz = size(); if (index < 0 || index >= sz) { throw new NoSuchElementException( @@ -255,12 +258,12 @@ public E get(final int index) { * @throws NullPointerException if the given element is null */ @Override - public boolean offer(E element) { + public boolean offer(@NotNull E element) { return add(element); } @Override - public E poll() { + public @Nullable E poll() { if (isEmpty()) { return null; } @@ -268,7 +271,7 @@ public E poll() { } @Override - public E element() { + public @Nullable E element() { if (isEmpty()) { throw new NoSuchElementException("queue is empty"); } @@ -276,7 +279,7 @@ public E element() { } @Override - public E peek() { + public @Nullable E peek() { if (isEmpty()) { return null; } @@ -284,7 +287,7 @@ public E peek() { } @Override - public E remove() { + public @NotNull E remove() { if (isEmpty()) { throw new NoSuchElementException("queue is empty"); } @@ -336,7 +339,7 @@ private int decrement(int index) { * @return an iterator over this queue's elements */ @Override - public Iterator iterator() { + public @NotNull Iterator iterator() { return new Iterator() { private int index = start; diff --git a/sentry/src/main/java/io/sentry/Dsn.java b/sentry/src/main/java/io/sentry/Dsn.java index db409e1794..04f713fa60 100644 --- a/sentry/src/main/java/io/sentry/Dsn.java +++ b/sentry/src/main/java/io/sentry/Dsn.java @@ -2,46 +2,48 @@ import io.sentry.exception.InvalidDsnException; import java.net.URI; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; final class Dsn { - private final String projectId; - private final String path; - private final String secretKey; - private final String publicKey; - private final URI sentryUri; + private final @NotNull String projectId; + private final @Nullable String path; + private final @Nullable String secretKey; + private final @NotNull String publicKey; + private final @NotNull URI sentryUri; /* / The project ID which the authenticated user is bound to. */ - public String getProjectId() { + public @NotNull String getProjectId() { return projectId; } /* / An optional path of which Sentry is hosted */ - public String getPath() { + public @Nullable String getPath() { return path; } /* / The optional secret key to authenticate the SDK. */ - public String getSecretKey() { + public @Nullable String getSecretKey() { return secretKey; } /* / The required public key to authenticate the SDK. */ - public String getPublicKey() { + public @NotNull String getPublicKey() { return publicKey; } /* / The URI used to communicate with Sentry */ + @NotNull URI getSentryUri() { return sentryUri; } diff --git a/sentry/src/main/java/io/sentry/ISentryClient.java b/sentry/src/main/java/io/sentry/ISentryClient.java index 8ecce13192..45acd8a0cd 100644 --- a/sentry/src/main/java/io/sentry/ISentryClient.java +++ b/sentry/src/main/java/io/sentry/ISentryClient.java @@ -176,7 +176,8 @@ default void captureSession(Session session) { * @param hint SDK specific but provides high level information about the origin of the event * @return The Id (SentryId object) of the event */ - SentryId captureEnvelope(SentryEnvelope envelope, @Nullable Object hint); + @Nullable + SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Object hint); /** * Captures an envelope. @@ -184,7 +185,7 @@ default void captureSession(Session session) { * @param envelope the SentryEnvelope to send. * @return The Id (SentryId object) of the event */ - default SentryId captureEnvelope(SentryEnvelope envelope) { + default @Nullable SentryId captureEnvelope(@NotNull SentryEnvelope envelope) { return captureEnvelope(envelope, null); } diff --git a/sentry/src/main/java/io/sentry/NoOpSentryClient.java b/sentry/src/main/java/io/sentry/NoOpSentryClient.java index 5a57893e4c..da0f8d3a59 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryClient.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryClient.java @@ -38,7 +38,7 @@ public void captureUserFeedback(UserFeedback userFeedback) {} public void captureSession(Session session, @Nullable Object hint) {} @Override - public SentryId captureEnvelope(SentryEnvelope envelope, @Nullable Object hint) { + public SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Object hint) { return SentryId.EMPTY_ID; } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 8f8b31ce1c..0fa6860d86 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -210,7 +210,9 @@ private static boolean initConfigurations(final @NotNull SentryOptions options) // eg release, distinctId, sentryClientName // this should be after setting serializers - if (options.getCacheDirPath() != null && !options.getCacheDirPath().isEmpty()) { + if (options.getOutboxPath() != null + && options.getCacheDirPath() != null + && !options.getCacheDirPath().isEmpty()) { final File cacheDir = new File(options.getCacheDirPath()); cacheDir.mkdirs(); diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index abc0343497..c41235fb47 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -143,7 +143,7 @@ private boolean shouldApplyScopeData( return sentryId; } - private List getAttachmentsFromScope(@Nullable Scope scope) { + private @Nullable List getAttachmentsFromScope(@Nullable Scope scope) { if (scope != null) { return scope.getAttachments(); } else { @@ -378,7 +378,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Objec @ApiStatus.Internal @Override - public @Nullable SentryId captureEnvelope( + public @NotNull SentryId captureEnvelope( final @NotNull SentryEnvelope envelope, final @Nullable Object hint) { Objects.requireNonNull(envelope, "SentryEnvelope is required."); @@ -557,7 +557,7 @@ private void sortBreadcrumbsByDate( final @NotNull SentryBaseEvent event, final @NotNull Collection breadcrumbs) { final List sortedBreadcrumbs = event.getBreadcrumbs(); - if (!breadcrumbs.isEmpty()) { + if (sortedBreadcrumbs != null && !breadcrumbs.isEmpty()) { sortedBreadcrumbs.addAll(breadcrumbs); Collections.sort(sortedBreadcrumbs, sortBreadcrumbsByDate); } @@ -581,7 +581,9 @@ private void sortBreadcrumbsByDate( breadcrumb.setMessage("BeforeSend callback failed."); breadcrumb.setCategory("SentryClient"); breadcrumb.setLevel(SentryLevel.ERROR); - breadcrumb.setData("sentry:message", e.getMessage()); + if (e.getMessage() != null) { + breadcrumb.setData("sentry:message", e.getMessage()); + } event.addBreadcrumb(breadcrumb); } } diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeaderAdapter.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeaderAdapter.java index ee57e40096..eb091639d3 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeaderAdapter.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeaderAdapter.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.util.Locale; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class SentryEnvelopeItemHeaderAdapter extends TypeAdapter { @@ -41,7 +42,7 @@ public void write(JsonWriter writer, SentryEnvelopeItemHeader value) throws IOEx } @Override - public SentryEnvelopeItemHeader read(JsonReader reader) throws IOException { + public @Nullable SentryEnvelopeItemHeader read(JsonReader reader) throws IOException { if (reader.peek() == JsonToken.NULL) { reader.nextNull(); return null; @@ -63,7 +64,10 @@ public SentryEnvelopeItemHeader read(JsonReader reader) throws IOException { break; case "type": try { - type = SentryItemType.valueOf(StringUtils.capitalize(reader.nextString())); + final String nextString = StringUtils.capitalize(reader.nextString()); + if (nextString != null) { + type = SentryItemType.valueOf(nextString); + } } catch (IllegalArgumentException ignored) { // invalid type } diff --git a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java index be60a3ff05..7f482fe381 100644 --- a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java +++ b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java @@ -40,7 +40,7 @@ public void close() throws IOException { } @VisibleForTesting - @NotNull + @Nullable Thread getHook() { return thread; } diff --git a/sentry/src/main/java/io/sentry/UnknownPropertiesTypeAdapterFactory.java b/sentry/src/main/java/io/sentry/UnknownPropertiesTypeAdapterFactory.java index e9c9c5b5d6..1f772fa57e 100644 --- a/sentry/src/main/java/io/sentry/UnknownPropertiesTypeAdapterFactory.java +++ b/sentry/src/main/java/io/sentry/UnknownPropertiesTypeAdapterFactory.java @@ -17,6 +17,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import org.jetbrains.annotations.Nullable; final class UnknownPropertiesTypeAdapterFactory implements TypeAdapterFactory { @@ -29,7 +30,7 @@ static TypeAdapterFactory get() { } @Override - public TypeAdapter create(final Gson gson, final TypeToken typeToken) { + public @Nullable TypeAdapter create(final Gson gson, final TypeToken typeToken) { // Check if we can deal with the given type if (!IUnknownPropertiesConsumer.class.isAssignableFrom(typeToken.getRawType())) { return null; @@ -103,7 +104,7 @@ public void write(final JsonWriter out, final T value) throws IOException { } @Override - public T read(final JsonReader in) { + public @Nullable T read(final JsonReader in) { // In its simplest solution, we can just collect a JSON tree because its much easier to // process JsonParser parser = new JsonParser(); diff --git a/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt b/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt index 2391bb50b2..2ad2180937 100644 --- a/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt @@ -4,6 +4,7 @@ import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import kotlin.test.Test +import kotlin.test.assertNotNull class ShutdownHookIntegrationTest { @@ -43,8 +44,10 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() integration.register(fixture.hub, fixture.options) - integration.hook.start() - integration.hook.join() + assertNotNull(integration.hook) { + it.start() + it.join() + } verify(fixture.hub).close() } From 9642217c8f6f42870bc56be7e30bb0eed213e28e Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 16:03:20 +0200 Subject: [PATCH 23/40] Add nullability annotations to `SentryClient` --- .../api/sentry-test-support.api | 2 ++ .../main/kotlin/io/sentry/test/reflection.kt | 30 +++++++++++++++++-- .../main/java/io/sentry/ISentryClient.java | 30 ++++++++++--------- .../main/java/io/sentry/NoOpSentryClient.java | 7 +++-- .../src/main/java/io/sentry/SentryClient.java | 4 +-- .../java/io/sentry/NoOpSentryClientTest.kt | 13 ++++---- .../test/java/io/sentry/SentryClientTest.kt | 5 ++-- 7 files changed, 61 insertions(+), 30 deletions(-) diff --git a/sentry-test-support/api/sentry-test-support.api b/sentry-test-support/api/sentry-test-support.api index a9de7458be..99b4c1ca13 100644 --- a/sentry-test-support/api/sentry-test-support.api +++ b/sentry-test-support/api/sentry-test-support.api @@ -4,6 +4,8 @@ public final class io/sentry/test/AssertionsKt { } public final class io/sentry/test/ReflectionKt { + public static final fun containsMethod (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Z + public static final fun containsMethod (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Z public static final fun getCtor (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor; } diff --git a/sentry-test-support/src/main/kotlin/io/sentry/test/reflection.kt b/sentry-test-support/src/main/kotlin/io/sentry/test/reflection.kt index 6a77f3753d..f8b4df892e 100644 --- a/sentry-test-support/src/main/kotlin/io/sentry/test/reflection.kt +++ b/sentry-test-support/src/main/kotlin/io/sentry/test/reflection.kt @@ -8,11 +8,35 @@ inline fun T.injectForField(name: String, value: Any?) { .set(this, value) } -inline fun T.callMethod(name: String, parameterTypes: Class<*>, value: Any?) { - T::class.java.getDeclaredMethod(name, parameterTypes) - .invoke(this, value) +inline fun T.callMethod(name: String, parameterTypes: Class<*>, value: Any?): Any? { + val declaredMethod = try { + T::class.java.getDeclaredMethod(name, parameterTypes) + } catch (e: NoSuchMethodException) { + T::class.java.interfaces.first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, parameterTypes) + } + return declaredMethod.invoke(this, value) } +inline fun T.callMethod(name: String, parameterTypes: Array>, vararg value: Any?): Any? { + val declaredMethod = try { + T::class.java.getDeclaredMethod(name, *parameterTypes) + } catch (e: NoSuchMethodException) { + T::class.java.interfaces.first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, *parameterTypes) + } + return declaredMethod.invoke(this, *value) +} + +fun Class<*>.containsMethod(name: String, parameterTypes: Array>): Boolean = + try { + this.getDeclaredMethod(name, *parameterTypes) + true + } catch (e: NoSuchMethodException) { + false + } + +fun Class<*>.containsMethod(name: String, parameterTypes: Class<*>): Boolean = + containsMethod(name, arrayOf(parameterTypes)) + inline fun Any.getProperty(name: String): T = try { this::class.java.getDeclaredField(name) diff --git a/sentry/src/main/java/io/sentry/ISentryClient.java b/sentry/src/main/java/io/sentry/ISentryClient.java index 45acd8a0cd..c391d55b0a 100644 --- a/sentry/src/main/java/io/sentry/ISentryClient.java +++ b/sentry/src/main/java/io/sentry/ISentryClient.java @@ -24,7 +24,8 @@ public interface ISentryClient { * @param hint SDK specific but provides high level information about the origin of the event. * @return The Id (SentryId object) of the event. */ - SentryId captureEvent(SentryEvent event, @Nullable Scope scope, @Nullable Object hint); + @NotNull + SentryId captureEvent(@NotNull SentryEvent event, @Nullable Scope scope, @Nullable Object hint); /** Flushes out the queue for up to timeout seconds and disable the client. */ void close(); @@ -42,7 +43,7 @@ public interface ISentryClient { * @param event the event * @return The Id (SentryId object) of the event */ - default SentryId captureEvent(SentryEvent event) { + default @NotNull SentryId captureEvent(@NotNull SentryEvent event) { return captureEvent(event, null, null); } @@ -53,7 +54,7 @@ default SentryId captureEvent(SentryEvent event) { * @param scope An optional scope to be applied to the event. * @return The Id (SentryId object) of the event */ - default SentryId captureEvent(SentryEvent event, @Nullable Scope scope) { + default @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Scope scope) { return captureEvent(event, scope, null); } @@ -64,7 +65,7 @@ default SentryId captureEvent(SentryEvent event, @Nullable Scope scope) { * @param hint SDK specific but provides high level information about the origin of the event. * @return The Id (SentryId object) of the event. */ - default SentryId captureEvent(SentryEvent event, @Nullable Object hint) { + default @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Object hint) { return captureEvent(event, null, hint); } @@ -76,7 +77,8 @@ default SentryId captureEvent(SentryEvent event, @Nullable Object hint) { * @param scope An optional scope to be applied to the event. * @return The Id (SentryId object) of the event */ - default SentryId captureMessage(String message, SentryLevel level, @Nullable Scope scope) { + default @NotNull SentryId captureMessage( + @NotNull String message, @NotNull SentryLevel level, @Nullable Scope scope) { SentryEvent event = new SentryEvent(); Message sentryMessage = new Message(); sentryMessage.setFormatted(message); @@ -93,7 +95,7 @@ default SentryId captureMessage(String message, SentryLevel level, @Nullable Sco * @param level The message level. * @return The Id (SentryId object) of the event */ - default SentryId captureMessage(String message, SentryLevel level) { + default @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { return captureMessage(message, level, null); } @@ -103,7 +105,7 @@ default SentryId captureMessage(String message, SentryLevel level) { * @param throwable The exception. * @return The Id (SentryId object) of the event */ - default SentryId captureException(Throwable throwable) { + default @NotNull SentryId captureException(@NotNull Throwable throwable) { return captureException(throwable, null, null); } @@ -115,8 +117,8 @@ default SentryId captureException(Throwable throwable) { * @param scope An optional scope to be applied to the event. * @return The Id (SentryId object) of the event */ - default SentryId captureException( - Throwable throwable, @Nullable Scope scope, @Nullable Object hint) { + default @NotNull SentryId captureException( + @NotNull Throwable throwable, @Nullable Scope scope, @Nullable Object hint) { SentryEvent event = new SentryEvent(throwable); return captureEvent(event, scope, hint); } @@ -128,7 +130,7 @@ default SentryId captureException( * @param hint SDK specific but provides high level information about the origin of the event * @return The Id (SentryId object) of the event */ - default SentryId captureException(Throwable throwable, @Nullable Object hint) { + default @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Object hint) { return captureException(throwable, null, hint); } @@ -139,7 +141,7 @@ default SentryId captureException(Throwable throwable, @Nullable Object hint) { * @param scope An optional scope to be applied to the event. * @return The Id (SentryId object) of the event */ - default SentryId captureException(Throwable throwable, @Nullable Scope scope) { + default @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Scope scope) { return captureException(throwable, scope, null); } @@ -148,7 +150,7 @@ default SentryId captureException(Throwable throwable, @Nullable Scope scope) { * * @param userFeedback The user feedback to send to Sentry. */ - void captureUserFeedback(UserFeedback userFeedback); + void captureUserFeedback(@NotNull UserFeedback userFeedback); /** * Captures a session. This method transform a session to an envelope and forwards to @@ -157,7 +159,7 @@ default SentryId captureException(Throwable throwable, @Nullable Scope scope) { * @param hint SDK specific but provides high level information about the origin of the event * @param session the Session */ - void captureSession(Session session, @Nullable Object hint); + void captureSession(@NotNull Session session, @Nullable Object hint); /** * Captures a session. This method transform a session to an envelope and forwards to @@ -165,7 +167,7 @@ default SentryId captureException(Throwable throwable, @Nullable Scope scope) { * * @param session the Session */ - default void captureSession(Session session) { + default void captureSession(@NotNull Session session) { captureSession(session, null); } diff --git a/sentry/src/main/java/io/sentry/NoOpSentryClient.java b/sentry/src/main/java/io/sentry/NoOpSentryClient.java index da0f8d3a59..eccb05da83 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryClient.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryClient.java @@ -21,7 +21,8 @@ public boolean isEnabled() { } @Override - public SentryId captureEvent(SentryEvent event, @Nullable Scope scope, @Nullable Object hint) { + public @NotNull SentryId captureEvent( + @NotNull SentryEvent event, @Nullable Scope scope, @Nullable Object hint) { return SentryId.EMPTY_ID; } @@ -32,10 +33,10 @@ public void close() {} public void flush(long timeoutMillis) {} @Override - public void captureUserFeedback(UserFeedback userFeedback) {} + public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} @Override - public void captureSession(Session session, @Nullable Object hint) {} + public void captureSession(@NotNull Session session, @Nullable Object hint) {} @Override public SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Object hint) { diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index c41235fb47..0470820fbd 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -394,7 +394,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Objec @Override public @NotNull SentryId captureTransaction( @NotNull SentryTransaction transaction, - final @NotNull Scope scope, + final @Nullable Scope scope, final @Nullable Object hint) { Objects.requireNonNull(transaction, "Transaction is required."); @@ -505,7 +505,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Objec return event; } - private T applyScope( + private @NotNull T applyScope( final @NotNull T sentryBaseEvent, final @Nullable Scope scope) { if (scope != null) { if (sentryBaseEvent.getRequest() == null) { diff --git a/sentry/src/test/java/io/sentry/NoOpSentryClientTest.kt b/sentry/src/test/java/io/sentry/NoOpSentryClientTest.kt index ed436db5d1..834959e1bf 100644 --- a/sentry/src/test/java/io/sentry/NoOpSentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/NoOpSentryClientTest.kt @@ -2,6 +2,7 @@ package io.sentry import com.nhaarman.mockitokotlin2.mock import io.sentry.protocol.SentryId +import io.sentry.test.callMethod import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -14,32 +15,32 @@ class NoOpSentryClientTest { @Test fun `captureEvent is returns empty SentryId`() = - assertEquals(SentryId.EMPTY_ID, sut.captureEvent(null)) + assertEquals(SentryId.EMPTY_ID, sut.callMethod("captureEvent", SentryEvent::class.java, null)) @Test fun `captureException is returns empty SentryId`() = - assertEquals(SentryId.EMPTY_ID, sut.captureException(null)) + assertEquals(SentryId.EMPTY_ID, sut.callMethod("captureException", Throwable::class.java, null)) @Test fun `captureMessage is returns empty SentryId`() = - assertEquals(SentryId.EMPTY_ID, sut.captureMessage(null, null)) + assertEquals(SentryId.EMPTY_ID, sut.callMethod("captureMessage", parameterTypes = arrayOf(String::class.java, SentryLevel::class.java), null, null)) @Test fun `close does not affect captureEvent`() { sut.close() - assertEquals(SentryId.EMPTY_ID, sut.captureEvent(null)) + assertEquals(SentryId.EMPTY_ID, sut.callMethod("captureEvent", SentryEvent::class.java, null)) } @Test fun `close does not affect captureException`() { sut.close() - assertEquals(SentryId.EMPTY_ID, sut.captureException(null)) + assertEquals(SentryId.EMPTY_ID, sut.callMethod("captureException", Throwable::class.java, null)) } @Test fun `close does not affect captureMessage`() { sut.close() - assertEquals(SentryId.EMPTY_ID, sut.captureMessage(null, null)) + assertEquals(SentryId.EMPTY_ID, sut.callMethod("captureMessage", parameterTypes = arrayOf(String::class.java, SentryLevel::class.java), null, null)) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 93dfce9923..0cad53251d 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -24,6 +24,7 @@ import io.sentry.protocol.SentryException import io.sentry.protocol.SentryId import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User +import io.sentry.test.callMethod import io.sentry.transport.ITransport import io.sentry.transport.ITransportGate import java.io.ByteArrayInputStream @@ -199,7 +200,7 @@ class SentryClientTest { fixture.sentryOptions.setBeforeSend { e, _ -> sentEvent = e; e } val sut = fixture.getSut() val actual = "actual message" - sut.captureMessage(actual, null) + sut.callMethod("captureMessage", parameterTypes = arrayOf(String::class.java, SentryLevel::class.java, Scope::class.java), actual, null, null) assertEquals(actual, sentEvent!!.message!!.formatted) } @@ -208,7 +209,7 @@ class SentryClientTest { var sentEvent: SentryEvent? = null fixture.sentryOptions.setBeforeSend { e, _ -> sentEvent = e; e } val sut = fixture.getSut() - sut.captureMessage(null, SentryLevel.DEBUG) + sut.callMethod("captureMessage", parameterTypes = arrayOf(String::class.java, SentryLevel::class.java), null, SentryLevel.DEBUG) assertEquals(SentryLevel.DEBUG, sentEvent!!.level) } From 90cba1ad42f29f190e14548516687956c2764788 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 16:54:42 +0200 Subject: [PATCH 24/40] Add nullability annotations. --- .../main/java/io/sentry/EnvelopeSender.java | 7 +++- sentry/src/main/java/io/sentry/Hub.java | 6 +++- .../java/io/sentry/MainEventProcessor.java | 10 +++--- .../SendFireAndForgetEnvelopeSender.java | 2 +- .../sentry/SendFireAndForgetOutboxSender.java | 2 +- sentry/src/main/java/io/sentry/Sentry.java | 2 +- .../src/main/java/io/sentry/SentryClient.java | 7 +++- .../java/io/sentry/SentryEnvelopeItem.java | 11 +++++-- .../src/main/java/io/sentry/SentryEvent.java | 7 ++-- .../main/java/io/sentry/SessionAdapter.java | 6 ++-- .../java/io/sentry/cache/CacheStrategy.java | 33 ++++++++----------- 11 files changed, 53 insertions(+), 40 deletions(-) diff --git a/sentry/src/main/java/io/sentry/EnvelopeSender.java b/sentry/src/main/java/io/sentry/EnvelopeSender.java index 26b4ab771d..27e1bf942a 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/EnvelopeSender.java @@ -56,7 +56,12 @@ protected void processFile(@NotNull File file, @Nullable Object hint) { try (final InputStream is = new BufferedInputStream(new FileInputStream(file))) { SentryEnvelope envelope = serializer.deserializeEnvelope(is); - hub.captureEnvelope(envelope, hint); + if (envelope == null) { + logger.log( + SentryLevel.ERROR, "Failed to deserialize cached envelope %s", file.getAbsolutePath()); + } else { + hub.captureEnvelope(envelope, hint); + } if (hint instanceof Flushable) { if (!((Flushable) hint).waitFlush()) { diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index 82b3dddba6..b3e6b5bfdc 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -135,7 +135,11 @@ public boolean isEnabled() { "Instance is disabled and this 'captureEnvelope' call is a no-op."); } else { try { - sentryId = stack.peek().getClient().captureEnvelope(envelope, hint); + final SentryId capturedEnvelopeId = + stack.peek().getClient().captureEnvelope(envelope, hint); + if (capturedEnvelopeId != null) { + sentryId = capturedEnvelopeId; + } } catch (Exception e) { options.getLogger().log(SentryLevel.ERROR, "Error while capturing envelope.", e); } diff --git a/sentry/src/main/java/io/sentry/MainEventProcessor.java b/sentry/src/main/java/io/sentry/MainEventProcessor.java index 7e08dbc5e7..7dab5177ed 100644 --- a/sentry/src/main/java/io/sentry/MainEventProcessor.java +++ b/sentry/src/main/java/io/sentry/MainEventProcessor.java @@ -191,11 +191,10 @@ private void setThreads(final @NotNull SentryEvent event) { // crashed properly List mechanismThreadIds = null; - final boolean hasExceptions = - event.getExceptions() != null && !event.getExceptions().isEmpty(); + final List eventExceptions = event.getExceptions(); - if (hasExceptions) { - for (final SentryException item : event.getExceptions()) { + if (eventExceptions != null && !eventExceptions.isEmpty()) { + for (final SentryException item : eventExceptions) { if (item.getMechanism() != null && item.getThreadId() != null) { if (mechanismThreadIds == null) { mechanismThreadIds = new ArrayList<>(); @@ -207,7 +206,8 @@ private void setThreads(final @NotNull SentryEvent event) { if (options.isAttachThreads()) { event.setThreads(sentryThreadFactory.getCurrentThreads(mechanismThreadIds)); - } else if (options.isAttachStacktrace() && !hasExceptions) { + } else if (options.isAttachStacktrace() + && (eventExceptions == null || eventExceptions.isEmpty())) { // when attachStacktrace is enabled, we attach only the current thread and its stack traces, // if there are no exceptions, exceptions have its own stack traces. event.setThreads(sentryThreadFactory.getCurrentThread()); diff --git a/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java b/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java index 1e90cabc19..ecf4cb7913 100644 --- a/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java @@ -26,7 +26,7 @@ public SendFireAndForgetEnvelopeSender( Objects.requireNonNull(options, "SentryOptions is required"); final String dirPath = sendFireAndForgetDirPath.getDirPath(); - if (!hasValidPath(dirPath, options.getLogger())) { + if (dirPath == null || !hasValidPath(dirPath, options.getLogger())) { options.getLogger().log(SentryLevel.ERROR, "No cache dir path is defined in options."); return null; } diff --git a/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java b/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java index 766b9fa4eb..0666b37cda 100644 --- a/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java +++ b/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java @@ -26,7 +26,7 @@ public SendFireAndForgetOutboxSender( Objects.requireNonNull(options, "SentryOptions is required"); final String dirPath = sendFireAndForgetDirPath.getDirPath(); - if (!hasValidPath(dirPath, options.getLogger())) { + if (dirPath == null || !hasValidPath(dirPath, options.getLogger())) { options.getLogger().log(SentryLevel.ERROR, "No outbox dir path is defined in options."); return null; } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 0fa6860d86..e81160281a 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -305,7 +305,7 @@ public static synchronized void close() { * * @param userFeedback The user feedback to send to Sentry. */ - public static void captureUserFeedback(UserFeedback userFeedback) { + public static void captureUserFeedback(final @NotNull UserFeedback userFeedback) { getCurrentHub().captureUserFeedback(userFeedback); } diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index 0470820fbd..b9b5755a76 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -388,7 +388,12 @@ public void captureSession(final @NotNull Session session, final @Nullable Objec options.getLogger().log(SentryLevel.ERROR, "Failed to capture envelope.", e); return SentryId.EMPTY_ID; } - return envelope.getHeader().getEventId(); + final SentryId eventId = envelope.getHeader().getEventId(); + if (eventId != null) { + return eventId; + } else { + return SentryId.EMPTY_ID; + } } @Override diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java index 28a24999e6..48715776f2 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -48,6 +48,7 @@ public final class SentryEnvelopeItem { } // TODO: Should be a Stream + @SuppressWarnings("NullAway") public @NotNull byte[] getData() throws Exception { if (data == null && dataFactory != null) { data = dataFactory.call(); @@ -247,11 +248,15 @@ public CachedItem(final @Nullable Callable dataFactory) { this.dataFactory = dataFactory; } - public @Nullable byte[] getBytes() throws Exception { - if (bytes == null) { + public @NotNull byte[] getBytes() throws Exception { + if (bytes == null && dataFactory != null) { bytes = dataFactory.call(); } - return bytes; + return orEmptyArray(bytes); + } + + private static @NotNull byte[] orEmptyArray(final @Nullable byte[] bytes) { + return bytes != null ? bytes : new byte[] {}; } } } diff --git a/sentry/src/main/java/io/sentry/SentryEvent.java b/sentry/src/main/java/io/sentry/SentryEvent.java index 0b3dea90aa..d72132da67 100644 --- a/sentry/src/main/java/io/sentry/SentryEvent.java +++ b/sentry/src/main/java/io/sentry/SentryEvent.java @@ -134,16 +134,15 @@ public void setLogger(final @Nullable String logger) { } } - public void setThreads(List threads) { + public void setThreads(final @Nullable List threads) { this.threads = new SentryValues<>(threads); } - @Nullable - public List getExceptions() { + public @Nullable List getExceptions() { return exception == null ? null : exception.getValues(); } - public void setExceptions(List exception) { + public void setExceptions(final @Nullable List exception) { this.exception = new SentryValues<>(exception); } diff --git a/sentry/src/main/java/io/sentry/SessionAdapter.java b/sentry/src/main/java/io/sentry/SessionAdapter.java index 3233ef0415..2f12d360a1 100644 --- a/sentry/src/main/java/io/sentry/SessionAdapter.java +++ b/sentry/src/main/java/io/sentry/SessionAdapter.java @@ -110,7 +110,7 @@ private boolean initAttrs(JsonWriter writer, boolean hasInitAtts) throws IOExcep } @Override - public Session read(JsonReader reader) throws IOException { + public @Nullable Session read(JsonReader reader) throws IOException { if (reader.peek() == JsonToken.NULL) { reader.nextNull(); return null; @@ -155,7 +155,9 @@ public Session read(JsonReader reader) throws IOException { String statusStr = null; try { statusStr = StringUtils.capitalize(reader.nextString()); - status = Session.State.valueOf(statusStr); + if (statusStr != null) { + status = Session.State.valueOf(statusStr); + } } catch (IllegalArgumentException e) { options.getLogger().log(SentryLevel.ERROR, "%s status is not valid.", statusStr); } diff --git a/sentry/src/main/java/io/sentry/cache/CacheStrategy.java b/sentry/src/main/java/io/sentry/cache/CacheStrategy.java index ecc6c26fe5..e83e79011b 100644 --- a/sentry/src/main/java/io/sentry/cache/CacheStrategy.java +++ b/sentry/src/main/java/io/sentry/cache/CacheStrategy.java @@ -120,13 +120,13 @@ private void moveInitFlagIfNecessary( final @NotNull File currentFile, final @NotNull File[] notDeletedFiles) { final SentryEnvelope currentEnvelope = readEnvelope(currentFile); - if (!isValidEnvelope(currentEnvelope)) { + if (currentEnvelope != null && !isValidEnvelope(currentEnvelope)) { return; } final Session currentSession = getFirstSession(currentEnvelope); - if (!isValidSession(currentSession)) { + if (currentSession == null || !isValidSession(currentSession)) { return; } @@ -140,7 +140,7 @@ private void moveInitFlagIfNecessary( for (final File notDeletedFile : notDeletedFiles) { final SentryEnvelope envelope = readEnvelope(notDeletedFile); - if (!isValidEnvelope(envelope)) { + if (envelope == null || !isValidEnvelope(envelope)) { continue; } @@ -156,7 +156,7 @@ private void moveInitFlagIfNecessary( final Session session = readSession(envelopeItem); - if (!isValidSession(session)) { + if (session == null || !isValidSession(session)) { continue; } @@ -168,7 +168,8 @@ private void moveInitFlagIfNecessary( return; } - if (currentSession.getSessionId().equals(session.getSessionId())) { + if (currentSession.getSessionId() != null + && currentSession.getSessionId().equals(session.getSessionId())) { session.setInitAsTrue(); try { newSessionItem = SentryEnvelopeItem.fromSession(serializer, session); @@ -218,7 +219,10 @@ private void moveInitFlagIfNecessary( return null; } - private @Nullable Session getFirstSession(final @NotNull SentryEnvelope envelope) { + private @Nullable Session getFirstSession(final @Nullable SentryEnvelope envelope) { + if (envelope == null) { + return null; + } for (final SentryEnvelopeItem item : envelope.getItems()) { if (!isSessionType(item)) { continue; @@ -230,21 +234,14 @@ private void moveInitFlagIfNecessary( return null; } - private boolean isValidSession(final @Nullable Session session) { - if (session == null) { - return false; - } - + private boolean isValidSession(final @NotNull Session session) { if (!session.getStatus().equals(Session.State.Ok)) { return false; } final UUID sessionId = session.getSessionId(); - if (sessionId == null) { - return false; - } - return true; + return sessionId != null; } private boolean isSessionType(final @Nullable SentryEnvelopeItem item) { @@ -289,11 +286,7 @@ private void saveNewEnvelope( return new SentryEnvelope(envelope.getHeader(), newEnvelopeItems); } - private boolean isValidEnvelope(final @Nullable SentryEnvelope envelope) { - if (envelope == null) { - return false; - } - + private boolean isValidEnvelope(final @NotNull SentryEnvelope envelope) { if (!envelope.getItems().iterator().hasNext()) { return false; } From 817e5b4f432d9a65b0cc363de2372637caf625ef Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 17:14:54 +0200 Subject: [PATCH 25/40] Polish tests. --- sentry/src/test/java/io/sentry/HubTest.kt | 86 +++++++++++++++++++ .../java/io/sentry/MainEventProcessorTest.kt | 49 +++++++---- 2 files changed, 119 insertions(+), 16 deletions(-) diff --git a/sentry/src/test/java/io/sentry/HubTest.kt b/sentry/src/test/java/io/sentry/HubTest.kt index f4c5429780..3f1a43251f 100644 --- a/sentry/src/test/java/io/sentry/HubTest.kt +++ b/sentry/src/test/java/io/sentry/HubTest.kt @@ -20,6 +20,7 @@ import io.sentry.hints.SessionStartHint import io.sentry.protocol.SentryId import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User +import io.sentry.test.callMethod import java.io.File import java.nio.file.Files import java.util.Queue @@ -34,6 +35,7 @@ import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue +import kotlin.test.fail class HubTest { @@ -256,6 +258,17 @@ class HubTest { } //region captureEvent tests + @Test + fun `when captureEvent is called and event is null, lastEventId is empty`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = Hub(options) + sut.callMethod("captureEvent", SentryEvent::class.java, null) + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + @Test fun `when captureEvent is called on disabled client, do nothing`() { val (sut, mockClient) = getEnabledHub() @@ -345,6 +358,17 @@ class HubTest { //endregion //region captureMessage tests + @Test + fun `when captureMessage is called and event is null, lastEventId is empty`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = Hub(options) + sut.callMethod("captureMessage", String::class.java, null) + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + @Test fun `when captureMessage is called on disabled client, do nothing`() { val (sut, mockClient) = getEnabledHub() @@ -371,6 +395,17 @@ class HubTest { //endregion //region captureException tests + @Test + fun `when captureException is called and exception is null, lastEventId is empty`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = Hub(options) + sut.callMethod("captureException", Throwable::class.java, null) + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + @Test fun `when captureException is called on disabled client, do nothing`() { val (sut, mockClient) = getEnabledHub() @@ -666,6 +701,18 @@ class HubTest { assertEquals(0, scope?.fingerprint?.count()) } + @Test + fun `when setFingerprint is called with null parameter, do nothing`() { + val hub = generateHub() + var scope: Scope? = null + hub.configureScope { + scope = it + } + + hub.callMethod("setFingerprint", List::class.java, null) + assertEquals(0, scope?.fingerprint?.count()) + } + @Test fun `when setFingerprint is called, fingerprint is set`() { val hub = generateHub() @@ -726,6 +773,18 @@ class HubTest { assertEquals(0, scope?.tags?.count()) } + @Test + fun `when setTag is called with null parameters, do nothing`() { + val hub = generateHub() + var scope: Scope? = null + hub.configureScope { + scope = it + } + + hub.callMethod("setTag", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) + assertEquals(0, scope?.tags?.count()) + } + @Test fun `when setTag is called, tag is set`() { val hub = generateHub() @@ -753,6 +812,18 @@ class HubTest { assertEquals(0, scope?.extras?.count()) } + @Test + fun `when setExtra is called with null parameters, do nothing`() { + val hub = generateHub() + var scope: Scope? = null + hub.configureScope { + scope = it + } + + hub.callMethod("setExtra", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) + assertEquals(0, scope?.extras?.count()) + } + @Test fun `when setExtra is called, extra is set`() { val hub = generateHub() @@ -767,6 +838,21 @@ class HubTest { //endregion //region captureEnvelope tests + @Test + fun `when captureEnvelope is called and envelope is null, throws IllegalArgumentException`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = Hub(options) + try { + sut.callMethod("captureEnvelope", SentryEnvelope::class.java, null) + fail() + } catch (e: Exception) { + assertTrue(e.cause is java.lang.IllegalArgumentException) + } + } + @Test fun `when captureEnvelope is called on disabled client, do nothing`() { val options = SentryOptions() diff --git a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt index 5190fd8b20..0acf9813a2 100644 --- a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt @@ -64,8 +64,10 @@ class MainEventProcessorTest { assertNotNull(event.exceptions) { assertSame(crashedThread.id, it.first().threadId) - assertNotNull(it.first().mechanism) { - assertFalse(it.isHandled!!) + assertNotNull(it.first().mechanism) { mechanism -> + assertNotNull(mechanism.isHandled) { isHandled -> + assertFalse(isHandled) + } } } assertNotNull(event.threads) { @@ -81,7 +83,9 @@ class MainEventProcessorTest { var event = generateCrashedEvent(crashedThread) event = sut.process(event, null) - assertTrue(event.threads!!.any { it.isCrashed == true }) + assertNotNull(event.threads) { threads -> + assertTrue(threads.any { it.isCrashed == true }) + } } @Test @@ -95,7 +99,9 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads!!.first { t -> t.id == crashedThread.id }.isCrashed == true) + assertNotNull(event.threads) { + assertTrue(it.first { t -> t.id == crashedThread.id }.isCrashed == true) + } } @Test @@ -109,7 +115,9 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads!!.first { t -> t.id == crashedThread.id }.isCrashed == true) + assertNotNull(event.threads) { + assertTrue(it.first { t -> t.id == crashedThread.id }.isCrashed == true) + } } @Test @@ -154,7 +162,9 @@ class MainEventProcessorTest { assertEquals("environment", event.environment) assertEquals("dist", event.dist) assertEquals("server", event.serverName) - assertTrue(event.threads!!.first { t -> t.id == crashedThread.id }.isCrashed == true) + assertNotNull(event.threads) { + assertTrue(it.first { t -> t.id == crashedThread.id }.isCrashed == true) + } } @Test @@ -184,7 +194,9 @@ class MainEventProcessorTest { var event = SentryEvent() event = sut.process(event, null) - assertEquals(1, event.threads!!.count()) + assertNotNull(event.threads) { + assertEquals(1, it.count()) + } } @Test @@ -202,13 +214,14 @@ class MainEventProcessorTest { val sut = fixture.getSut() val event = SentryEvent() sut.process(event, null) - assertNotNull(event.sdk) - assertEquals(event.sdk!!.name, "test") - assertEquals(event.sdk!!.version, "1.2.3") + assertNotNull(event.sdk) { + assertEquals(it.name, "test") + assertEquals(it.version, "1.2.3") + } } @Test - fun `when event and SentryOptions do not have environment set, sets "production" as environment`() { + fun `when event and SentryOptions do not have environment set, sets production as environment`() { val sut = fixture.getSut(environment = null) val event = SentryEvent() sut.process(event, null) @@ -216,7 +229,7 @@ class MainEventProcessorTest { } @Test - fun `when event does not have ip address set and sendDefaultPii is set to true, sets "{{auto}}" as the ip address`() { + fun `when event does not have ip address set and sendDefaultPii is set to true, sets {{auto}} as the ip address`() { val sut = fixture.getSut(sendDefaultPii = true) val event = SentryEvent() sut.process(event, null) @@ -271,8 +284,10 @@ class MainEventProcessorTest { val sut = fixture.getSut(tags = mapOf("tag1" to "value1", "tag2" to "value2")) val event = SentryEvent() sut.process(event, null) - assertEquals("value1", event.tags!!["tag1"]) - assertEquals("value2", event.tags!!["tag2"]) + assertNotNull(event.tags) { + assertEquals("value1", it["tag1"]) + assertEquals("value2", it["tag2"]) + } } @Test @@ -281,8 +296,10 @@ class MainEventProcessorTest { val event = SentryEvent() event.setTag("tag2", "event-tag-value") sut.process(event, null) - assertEquals("value1", event.tags!!["tag1"]) - assertEquals("event-tag-value", event.tags!!["tag2"]) + assertNotNull(event.tags) { + assertEquals("value1", it["tag1"]) + assertEquals("event-tag-value", it["tag2"]) + } } @Test From 3c87b97b96b6f8f3a5dbd129efef6bbf8e135509 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 17:39:14 +0200 Subject: [PATCH 26/40] Handle `Scope#setTransaction` with `null` --- sentry/build.gradle.kts | 10 +++---- sentry/src/main/java/io/sentry/Scope.java | 13 ++++---- sentry/src/test/java/io/sentry/ScopeTest.kt | 33 +++++++++++++++++---- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/sentry/build.gradle.kts b/sentry/build.gradle.kts index d6641e5d2a..56f71494ba 100644 --- a/sentry/build.gradle.kts +++ b/sentry/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { errorprone(Config.CompileOnly.errorprone) errorproneJavac(Config.CompileOnly.errorProneJavac8) compileOnly(Config.CompileOnly.jetbrainsAnnotations) -// errorprone(Config.CompileOnly.errorProneNullAway) + errorprone(Config.CompileOnly.errorProneNullAway) // tests testImplementation(kotlin(Config.kotlinStdLib)) @@ -84,8 +84,8 @@ buildConfig { val generateBuildConfig by tasks tasks.withType() { dependsOn(generateBuildConfig) -// options.errorprone { -// check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) -// option("NullAway:AnnotatedPackages", "io.sentry") -// } + options.errorprone { + check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) + option("NullAway:AnnotatedPackages", "io.sentry") + } } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index f7aeef766a..22e5abf05b 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -112,11 +112,14 @@ public void setLevel(final @Nullable SentryLevel level) { * * @param transaction the transaction */ - // todo: make nullable, unset transaction if null - public void setTransaction(final @NotNull String transaction) { + public void setTransaction(final @Nullable String transaction) { final ITransaction tx = this.transaction; if (tx != null) { - tx.setName(transaction); + if (transaction != null) { + tx.setName(transaction); + } else { + this.setTransaction((ITransaction) null); + } } this.transactionName = transaction; } @@ -144,9 +147,9 @@ public ISpan getSpan() { * * @param transaction the transaction */ - public void setTransaction(final @NotNull ITransaction transaction) { + public void setTransaction(final @Nullable ITransaction transaction) { synchronized (transactionLock) { - this.transaction = Objects.requireNonNull(transaction, "transaction is required"); + this.transaction = transaction; } } diff --git a/sentry/src/test/java/io/sentry/ScopeTest.kt b/sentry/src/test/java/io/sentry/ScopeTest.kt index b6806362c7..e6c1051c33 100644 --- a/sentry/src/test/java/io/sentry/ScopeTest.kt +++ b/sentry/src/test/java/io/sentry/ScopeTest.kt @@ -636,7 +636,7 @@ class ScopeTest { fun `Scope getTransaction returns the transaction if there is no active span`() { val scope = Scope(SentryOptions()) val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) - scope.setTransaction(transaction) + scope.transaction = transaction assertEquals(transaction, scope.span) } @@ -644,7 +644,7 @@ class ScopeTest { fun `Scope getTransaction returns the current span if there is an unfinished span`() { val scope = Scope(SentryOptions()) val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) - scope.setTransaction(transaction) + scope.transaction = transaction val span = transaction.startChild("op") assertEquals(span, scope.span) } @@ -653,7 +653,7 @@ class ScopeTest { fun `Scope getTransaction returns the current span if there is a finished span`() { val scope = Scope(SentryOptions()) val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) - scope.setTransaction(transaction) + scope.transaction = transaction val span = transaction.startChild("op") span.finish() assertEquals(transaction, scope.span) @@ -663,12 +663,35 @@ class ScopeTest { fun `Scope getTransaction returns the latest span if there is a list of active span`() { val scope = Scope(SentryOptions()) val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) - scope.setTransaction(transaction) + scope.transaction = transaction val span = transaction.startChild("op") val innerSpan = span.startChild("op") assertEquals(innerSpan, scope.span) } + @Test + fun `Scope setTransaction sets transaction name`() { + val scope = Scope(SentryOptions()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) + scope.transaction = transaction + scope.setTransaction("new-name") + assertNotNull(scope.transaction) { + assertEquals("new-name", it.name) + } + assertEquals("new-name", scope.transactionName) + } + + @Test + fun `Scope setTransaction with null clears transaction`() { + val scope = Scope(SentryOptions()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) + scope.transaction = transaction + // todo: potentially we can improve the api here + scope.setTransaction(null as String?) + assertNull(scope.transaction) + assertNull(scope.transactionName) + } + @Test fun `attachments are thread safe`() { val scope = Scope(SentryOptions()) @@ -713,7 +736,7 @@ class ScopeTest { fun `when transaction is started, sets transaction name on the transaction object`() { val scope = Scope(SentryOptions()) val sentryTransaction = SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) - scope.setTransaction(sentryTransaction) + scope.transaction = sentryTransaction assertEquals("transaction-name", scope.transactionName) scope.setTransaction("new-name") assertEquals("new-name", scope.transactionName) From 7aa2add225c352fb903a688e0aadc866f253fd6b Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 18:08:58 +0200 Subject: [PATCH 27/40] Polish. --- sentry/src/main/java/io/sentry/Breadcrumb.java | 2 +- .../io/sentry/DuplicateEventDetectionEventProcessor.java | 4 ++-- sentry/src/main/java/io/sentry/EnvelopeReader.java | 6 +++--- sentry/src/main/java/io/sentry/EnvelopeSender.java | 8 ++++---- sentry/src/main/java/io/sentry/Hub.java | 2 +- .../main/java/io/sentry/IUnknownPropertiesConsumer.java | 3 ++- sentry/src/main/java/io/sentry/OptionsContainer.java | 5 +++-- sentry/src/main/java/io/sentry/ScopeCallback.java | 4 +++- sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java | 8 +++++--- .../java/io/sentry/exception/SentryEnvelopeException.java | 4 +++- sentry/src/main/java/io/sentry/protocol/App.java | 2 +- sentry/src/main/java/io/sentry/protocol/Browser.java | 2 +- sentry/src/main/java/io/sentry/protocol/Device.java | 2 +- sentry/src/main/java/io/sentry/protocol/Gpu.java | 2 +- .../src/main/java/io/sentry/protocol/OperatingSystem.java | 2 +- sentry/src/main/java/io/sentry/protocol/SdkVersion.java | 2 +- .../src/main/java/io/sentry/protocol/SentryPackage.java | 2 +- .../src/main/java/io/sentry/protocol/SentryRuntime.java | 2 +- .../main/java/io/sentry/protocol/SentryStackTrace.java | 3 ++- sentry/src/main/java/io/sentry/protocol/User.java | 2 +- .../main/java/io/sentry/transport/NoOpEnvelopeCache.java | 4 ++-- 21 files changed, 40 insertions(+), 31 deletions(-) diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index ecf4202c66..0e4eedf93e 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -222,7 +222,7 @@ public void setLevel(@Nullable SentryLevel level) { */ @ApiStatus.Internal @Override - public void acceptUnknownProperties(@Nullable Map unknown) { + public void acceptUnknownProperties(@NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java b/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java index c985cb817c..4f74c00f63 100644 --- a/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java +++ b/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java @@ -11,9 +11,9 @@ /** Deduplicates events containing throwable that has been already processed. */ public final class DuplicateEventDetectionEventProcessor implements EventProcessor { - private final Map capturedObjects = + private final @NotNull Map capturedObjects = Collections.synchronizedMap(new WeakHashMap<>()); - private final SentryOptions options; + private final @NotNull SentryOptions options; public DuplicateEventDetectionEventProcessor(final @NotNull SentryOptions options) { this.options = Objects.requireNonNull(options, "options are required"); diff --git a/sentry/src/main/java/io/sentry/EnvelopeReader.java b/sentry/src/main/java/io/sentry/EnvelopeReader.java index 1cad61264a..563d0fb964 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeReader.java +++ b/sentry/src/main/java/io/sentry/EnvelopeReader.java @@ -137,13 +137,13 @@ SentryEnvelopeItemHeader.class, new SentryEnvelopeItemHeaderAdapter()) } } - private SentryEnvelopeHeader deserializeEnvelopeHeader(byte[] buffer, int offset, int length) { + private @Nullable SentryEnvelopeHeader deserializeEnvelopeHeader(final @NotNull byte[] buffer, int offset, int length) { String json = new String(buffer, offset, length, UTF_8); return gson.fromJson(json, SentryEnvelopeHeader.class); } - private SentryEnvelopeItemHeader deserializeEnvelopeItemHeader( - byte[] buffer, int offset, int length) { + private @Nullable SentryEnvelopeItemHeader deserializeEnvelopeItemHeader( + final @NotNull byte[] buffer, int offset, int length) { String json = new String(buffer, offset, length, UTF_8); return gson.fromJson(json, SentryEnvelopeItemHeader.class); } diff --git a/sentry/src/main/java/io/sentry/EnvelopeSender.java b/sentry/src/main/java/io/sentry/EnvelopeSender.java index 27e1bf942a..9af659bdee 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/EnvelopeSender.java @@ -34,7 +34,7 @@ public EnvelopeSender( } @Override - protected void processFile(@NotNull File file, @Nullable Object hint) { + protected void processFile(final @NotNull File file, final @Nullable Object hint) { if (!file.isFile()) { logger.log(SentryLevel.DEBUG, "'%s' is not a file.", file.getAbsolutePath()); return; @@ -102,18 +102,18 @@ protected void processFile(@NotNull File file, @Nullable Object hint) { } @Override - protected boolean isRelevantFileName(String fileName) { + protected boolean isRelevantFileName(final @NotNull String fileName) { return fileName.endsWith(EnvelopeCache.SUFFIX_ENVELOPE_FILE); } @Override - public void processEnvelopeFile(@NotNull String path, @Nullable Object hint) { + public void processEnvelopeFile(final @NotNull String path, final @Nullable Object hint) { Objects.requireNonNull(path, "Path is required."); processFile(new File(path), hint); } - private void safeDelete(File file, String errorMessageSuffix) { + private void safeDelete(final @NotNull File file, final @NotNull String errorMessageSuffix) { try { if (!file.delete()) { logger.log( diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index b3e6b5bfdc..d998a5c259 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -192,7 +192,7 @@ private void assignTraceContext(final @NotNull SentryEvent event) { } @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) { + public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { if (!isEnabled()) { options .getLogger() diff --git a/sentry/src/main/java/io/sentry/IUnknownPropertiesConsumer.java b/sentry/src/main/java/io/sentry/IUnknownPropertiesConsumer.java index 5da2df3020..fae9773001 100644 --- a/sentry/src/main/java/io/sentry/IUnknownPropertiesConsumer.java +++ b/sentry/src/main/java/io/sentry/IUnknownPropertiesConsumer.java @@ -2,8 +2,9 @@ import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; @ApiStatus.Internal public interface IUnknownPropertiesConsumer { - void acceptUnknownProperties(Map unknown); + void acceptUnknownProperties(@NotNull Map unknown); } diff --git a/sentry/src/main/java/io/sentry/OptionsContainer.java b/sentry/src/main/java/io/sentry/OptionsContainer.java index a3593c537b..e497e76174 100644 --- a/sentry/src/main/java/io/sentry/OptionsContainer.java +++ b/sentry/src/main/java/io/sentry/OptionsContainer.java @@ -2,6 +2,7 @@ import java.lang.reflect.InvocationTargetException; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; @ApiStatus.Internal public final class OptionsContainer { @@ -12,12 +13,12 @@ public static OptionsContainer create(final Class clazz) { private final Class clazz; - private OptionsContainer(final Class clazz) { + private OptionsContainer(final @NotNull Class clazz) { super(); this.clazz = clazz; } - public T createInstance() + public @NotNull T createInstance() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { return clazz.getDeclaredConstructor().newInstance(); diff --git a/sentry/src/main/java/io/sentry/ScopeCallback.java b/sentry/src/main/java/io/sentry/ScopeCallback.java index a20632c468..344b2835c1 100644 --- a/sentry/src/main/java/io/sentry/ScopeCallback.java +++ b/sentry/src/main/java/io/sentry/ScopeCallback.java @@ -1,5 +1,7 @@ package io.sentry; +import org.jetbrains.annotations.NotNull; + public interface ScopeCallback { - void run(Scope scope); + void run(@NotNull Scope scope); } diff --git a/sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java index a674c7f771..9fa8392b3a 100644 --- a/sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java @@ -1,15 +1,17 @@ package io.sentry.cache; import io.sentry.SentryEnvelope; + +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public interface IEnvelopeCache extends Iterable { - void store(SentryEnvelope envelope, @Nullable Object hint); + void store(@NotNull SentryEnvelope envelope, @Nullable Object hint); - default void store(SentryEnvelope envelope) { + default void store(@NotNull SentryEnvelope envelope) { store(envelope, null); } - void discard(SentryEnvelope envelope); + void discard(@NotNull SentryEnvelope envelope); } diff --git a/sentry/src/main/java/io/sentry/exception/SentryEnvelopeException.java b/sentry/src/main/java/io/sentry/exception/SentryEnvelopeException.java index 28c08bba0f..60f0902bb9 100644 --- a/sentry/src/main/java/io/sentry/exception/SentryEnvelopeException.java +++ b/sentry/src/main/java/io/sentry/exception/SentryEnvelopeException.java @@ -1,10 +1,12 @@ package io.sentry.exception; +import org.jetbrains.annotations.Nullable; + public final class SentryEnvelopeException extends Exception { private static final long serialVersionUID = -8307801916948173232L; - public SentryEnvelopeException(String message) { + public SentryEnvelopeException(final @Nullable String message) { super(message); } } diff --git a/sentry/src/main/java/io/sentry/protocol/App.java b/sentry/src/main/java/io/sentry/protocol/App.java index 682f280afb..cbc8f963ca 100644 --- a/sentry/src/main/java/io/sentry/protocol/App.java +++ b/sentry/src/main/java/io/sentry/protocol/App.java @@ -101,7 +101,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(Map unknown) { + public void acceptUnknownProperties(@NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/Browser.java b/sentry/src/main/java/io/sentry/protocol/Browser.java index 2924ce0f7b..e14586f095 100644 --- a/sentry/src/main/java/io/sentry/protocol/Browser.java +++ b/sentry/src/main/java/io/sentry/protocol/Browser.java @@ -43,7 +43,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @NotNull Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/Device.java b/sentry/src/main/java/io/sentry/protocol/Device.java index 4af0dba444..19c83998cc 100644 --- a/sentry/src/main/java/io/sentry/protocol/Device.java +++ b/sentry/src/main/java/io/sentry/protocol/Device.java @@ -352,7 +352,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @NotNull Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/Gpu.java b/sentry/src/main/java/io/sentry/protocol/Gpu.java index 69af5587b3..c8b28beeb6 100644 --- a/sentry/src/main/java/io/sentry/protocol/Gpu.java +++ b/sentry/src/main/java/io/sentry/protocol/Gpu.java @@ -118,7 +118,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @NotNull Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java b/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java index fcc9442986..6100355ac6 100644 --- a/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java +++ b/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java @@ -94,7 +94,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @NotNull Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/SdkVersion.java b/sentry/src/main/java/io/sentry/protocol/SdkVersion.java index 6b7ef32bba..55560765ec 100644 --- a/sentry/src/main/java/io/sentry/protocol/SdkVersion.java +++ b/sentry/src/main/java/io/sentry/protocol/SdkVersion.java @@ -108,7 +108,7 @@ public void addIntegration(final @NotNull String integration) { @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @Nullable Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryPackage.java b/sentry/src/main/java/io/sentry/protocol/SentryPackage.java index b77d45ce65..8ddba478c9 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryPackage.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryPackage.java @@ -49,7 +49,7 @@ public void setVersion(final @NotNull String version) { @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @Nullable Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java b/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java index d22b652f20..e198ab626e 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java @@ -60,7 +60,7 @@ Map getUnknown() { @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @NotNull Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryStackTrace.java b/sentry/src/main/java/io/sentry/protocol/SentryStackTrace.java index f2a1b7b297..b59f46fa41 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryStackTrace.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryStackTrace.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -85,7 +86,7 @@ public void setFrames(final @Nullable List frames) { @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @Nullable Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = unknown; } diff --git a/sentry/src/main/java/io/sentry/protocol/User.java b/sentry/src/main/java/io/sentry/protocol/User.java index aa29298e89..8cabf6aa78 100644 --- a/sentry/src/main/java/io/sentry/protocol/User.java +++ b/sentry/src/main/java/io/sentry/protocol/User.java @@ -139,7 +139,7 @@ public void setOthers(final @Nullable Map other) { */ @ApiStatus.Internal @Override - public void acceptUnknownProperties(final @NotNull Map unknown) { + public void acceptUnknownProperties(final @NotNull Map unknown) { this.unknown = new ConcurrentHashMap<>(unknown); } diff --git a/sentry/src/main/java/io/sentry/transport/NoOpEnvelopeCache.java b/sentry/src/main/java/io/sentry/transport/NoOpEnvelopeCache.java index 712623b173..d48b083a0c 100644 --- a/sentry/src/main/java/io/sentry/transport/NoOpEnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/transport/NoOpEnvelopeCache.java @@ -15,10 +15,10 @@ public static NoOpEnvelopeCache getInstance() { } @Override - public void store(SentryEnvelope envelope, @Nullable Object hint) {} + public void store(@NotNull SentryEnvelope envelope, @Nullable Object hint) {} @Override - public void discard(SentryEnvelope envelope) {} + public void discard(@NotNull SentryEnvelope envelope) {} @NotNull @Override From 099dbab9a0135085ab8f8f4bca9b7b94b1b3879a Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 3 May 2021 18:36:48 +0200 Subject: [PATCH 28/40] Polish. --- sentry/src/main/java/io/sentry/EnvelopeReader.java | 7 ++++++- sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/EnvelopeReader.java b/sentry/src/main/java/io/sentry/EnvelopeReader.java index 563d0fb964..e182de22cd 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeReader.java +++ b/sentry/src/main/java/io/sentry/EnvelopeReader.java @@ -87,6 +87,10 @@ SentryEnvelopeItemHeader.class, new SentryEnvelopeItemHeaderAdapter()) deserializeEnvelopeItemHeader( envelopeBytes, itemHeaderStartOffset, lineBreakIndex - itemHeaderStartOffset); + if (itemHeader == null) { + throw new IllegalArgumentException( + "Item header at index '" + items.size() + "' is null."); + } if (itemHeader.getLength() <= 0) { throw new IllegalArgumentException( "Item header at index '" @@ -137,7 +141,8 @@ SentryEnvelopeItemHeader.class, new SentryEnvelopeItemHeaderAdapter()) } } - private @Nullable SentryEnvelopeHeader deserializeEnvelopeHeader(final @NotNull byte[] buffer, int offset, int length) { + private @Nullable SentryEnvelopeHeader deserializeEnvelopeHeader( + final @NotNull byte[] buffer, int offset, int length) { String json = new String(buffer, offset, length, UTF_8); return gson.fromJson(json, SentryEnvelopeHeader.class); } diff --git a/sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java index 9fa8392b3a..eca86ca01a 100644 --- a/sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/IEnvelopeCache.java @@ -1,7 +1,6 @@ package io.sentry.cache; import io.sentry.SentryEnvelope; - import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; From 57d0c74570ad0b19a2f2ed405bc4ab9fd91b35a8 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Wed, 5 May 2021 16:16:43 +0200 Subject: [PATCH 29/40] Polish. --- sentry/src/main/java/io/sentry/IScopeObserver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/IScopeObserver.java b/sentry/src/main/java/io/sentry/IScopeObserver.java index baf06d71c5..1efd6d7cff 100644 --- a/sentry/src/main/java/io/sentry/IScopeObserver.java +++ b/sentry/src/main/java/io/sentry/IScopeObserver.java @@ -10,7 +10,7 @@ public interface IScopeObserver { void addBreadcrumb(@NotNull Breadcrumb crumb); - void setTag(@NotNull String key, @Nullable String value); + void setTag(@NotNull String key, @NotNull String value); void removeTag(@NotNull String key); From 66aa861d6b8a7c77b995e33bb31bebfb164078cc Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 12:20:44 +0200 Subject: [PATCH 30/40] Update sentry/src/main/java/io/sentry/cache/EnvelopeCache.java Co-authored-by: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com> --- sentry/src/main/java/io/sentry/cache/EnvelopeCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java index caacd79f1f..340759fa74 100644 --- a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java @@ -55,7 +55,7 @@ public final class EnvelopeCache extends CacheStrategy implements IEnvelopeCache private final @NotNull Map fileNameMap = new WeakHashMap<>(); - public static IEnvelopeCache create(final @NotNull SentryOptions options) { + public static @NotNull IEnvelopeCache create(final @NotNull SentryOptions options) { final String cacheDirPath = options.getCacheDirPath(); final int cacheDirSize = options.getCacheDirSize(); if (cacheDirPath == null) { From 246faf0d3efbdfe4740c7cadfe92a0f0307a1cc6 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 13:27:57 +0200 Subject: [PATCH 31/40] Fix logger nullability. --- .../io/sentry/android/core/AndroidLogger.java | 15 ++++++++------- .../src/main/java/io/sentry/DiagnosticLogger.java | 12 ++++++------ sentry/src/main/java/io/sentry/ILogger.java | 9 +++++---- sentry/src/main/java/io/sentry/NoOpLogger.java | 10 +++++----- .../src/main/java/io/sentry/SystemOutLogger.java | 12 ++++++------ .../test/java/io/sentry/DiagnosticLoggerTest.kt | 5 +++-- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java index 71fcc54460..5fc1b84af9 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidLogger.java @@ -3,6 +3,7 @@ import android.util.Log; import io.sentry.ILogger; import io.sentry.SentryLevel; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; final class AndroidLogger implements ILogger { @@ -12,8 +13,8 @@ final class AndroidLogger implements ILogger { @SuppressWarnings("AnnotateFormatMethod") @Override public void log( - final @Nullable SentryLevel level, - final @Nullable String message, + final @NotNull SentryLevel level, + final @NotNull String message, final @Nullable Object... args) { Log.println(toLogcatLevel(level), tag, String.format(message, args)); } @@ -21,17 +22,17 @@ public void log( @SuppressWarnings("AnnotateFormatMethod") @Override public void log( - final @Nullable SentryLevel level, + final @NotNull SentryLevel level, final @Nullable Throwable throwable, - final @Nullable String message, + final @NotNull String message, final @Nullable Object... args) { log(level, String.format(message, args), throwable); } @Override public void log( - final @Nullable SentryLevel level, - final @Nullable String message, + final @NotNull SentryLevel level, + final @NotNull String message, final @Nullable Throwable throwable) { switch (level) { @@ -59,7 +60,7 @@ public boolean isEnabled(@Nullable SentryLevel level) { return true; } - private int toLogcatLevel(final @Nullable SentryLevel sentryLevel) { + private int toLogcatLevel(final @NotNull SentryLevel sentryLevel) { switch (sentryLevel) { case INFO: return Log.INFO; diff --git a/sentry/src/main/java/io/sentry/DiagnosticLogger.java b/sentry/src/main/java/io/sentry/DiagnosticLogger.java index b37417c084..c96e061f2c 100644 --- a/sentry/src/main/java/io/sentry/DiagnosticLogger.java +++ b/sentry/src/main/java/io/sentry/DiagnosticLogger.java @@ -47,8 +47,8 @@ public boolean isEnabled(final @Nullable SentryLevel level) { */ @Override public void log( - final @Nullable SentryLevel level, - final @Nullable String message, + final @NotNull SentryLevel level, + final @NotNull String message, final @Nullable Object... args) { if (logger != null && isEnabled(level)) { logger.log(level, message, args); @@ -64,8 +64,8 @@ public void log( */ @Override public void log( - final @Nullable SentryLevel level, - final @Nullable String message, + final @NotNull SentryLevel level, + final @NotNull String message, final @Nullable Throwable throwable) { if (logger != null && isEnabled(level)) { logger.log(level, message, throwable); @@ -82,9 +82,9 @@ public void log( */ @Override public void log( - final @Nullable SentryLevel level, + final @NotNull SentryLevel level, final @Nullable Throwable throwable, - final @Nullable String message, + final @NotNull String message, final @Nullable Object... args) { if (logger != null && isEnabled(level)) { logger.log(level, throwable, message, args); diff --git a/sentry/src/main/java/io/sentry/ILogger.java b/sentry/src/main/java/io/sentry/ILogger.java index 373905cbb0..d709db01d1 100644 --- a/sentry/src/main/java/io/sentry/ILogger.java +++ b/sentry/src/main/java/io/sentry/ILogger.java @@ -1,5 +1,6 @@ package io.sentry; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** Sentry SDK internal logging interface. */ @@ -12,7 +13,7 @@ public interface ILogger { * @param message The message. * @param args The optional arguments to format the message. */ - void log(@Nullable SentryLevel level, @Nullable String message, @Nullable Object... args); + void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Object... args); /** * Logs a message with the specified level, message and optional arguments. @@ -21,7 +22,7 @@ public interface ILogger { * @param message The message. * @param throwable The throwable to log. */ - void log(@Nullable SentryLevel level, @Nullable String message, @Nullable Throwable throwable); + void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Throwable throwable); /** * Logs a message with the specified level, throwable, message and optional arguments. @@ -32,9 +33,9 @@ public interface ILogger { * @param args the formatting arguments */ void log( - @Nullable SentryLevel level, + @NotNull SentryLevel level, @Nullable Throwable throwable, - @Nullable String message, + @NotNull String message, @Nullable Object... args); /** diff --git a/sentry/src/main/java/io/sentry/NoOpLogger.java b/sentry/src/main/java/io/sentry/NoOpLogger.java index 20262abb67..9b036d8d77 100644 --- a/sentry/src/main/java/io/sentry/NoOpLogger.java +++ b/sentry/src/main/java/io/sentry/NoOpLogger.java @@ -1,5 +1,6 @@ package io.sentry; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** No-op implementation of ILogger */ @@ -14,18 +15,17 @@ public static NoOpLogger getInstance() { private NoOpLogger() {} @Override - public void log( - @Nullable SentryLevel level, @Nullable String message, @Nullable Object... args) {} + public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Object... args) {} @Override public void log( - @Nullable SentryLevel level, @Nullable String message, @Nullable Throwable throwable) {} + @NotNull SentryLevel level, @NotNull String message, @Nullable Throwable throwable) {} @Override public void log( - @Nullable SentryLevel level, + @NotNull SentryLevel level, @Nullable Throwable throwable, - @Nullable String message, + @NotNull String message, @Nullable Object... args) {} @Override diff --git a/sentry/src/main/java/io/sentry/SystemOutLogger.java b/sentry/src/main/java/io/sentry/SystemOutLogger.java index 5edcbde1ea..157912e56b 100644 --- a/sentry/src/main/java/io/sentry/SystemOutLogger.java +++ b/sentry/src/main/java/io/sentry/SystemOutLogger.java @@ -18,8 +18,8 @@ public final class SystemOutLogger implements ILogger { @SuppressWarnings("AnnotateFormatMethod") @Override public void log( - final @Nullable SentryLevel level, - final @Nullable String message, + final @NotNull SentryLevel level, + final @NotNull String message, final @Nullable Object... args) { System.out.println(String.format("%s: %s", level, String.format(message, args))); } @@ -34,8 +34,8 @@ public void log( @SuppressWarnings("AnnotateFormatMethod") @Override public void log( - final @Nullable SentryLevel level, - final @Nullable String message, + final @NotNull SentryLevel level, + final @NotNull String message, final @Nullable Throwable throwable) { if (throwable == null) { this.log(level, message); @@ -58,9 +58,9 @@ public void log( @SuppressWarnings("AnnotateFormatMethod") @Override public void log( - final @Nullable SentryLevel level, + final @NotNull SentryLevel level, final @Nullable Throwable throwable, - final @Nullable String message, + final @NotNull String message, final @Nullable Object... args) { if (throwable == null) { this.log(level, message, args); diff --git a/sentry/src/test/java/io/sentry/DiagnosticLoggerTest.kt b/sentry/src/test/java/io/sentry/DiagnosticLoggerTest.kt index df6661a3e9..bed4163fe7 100644 --- a/sentry/src/test/java/io/sentry/DiagnosticLoggerTest.kt +++ b/sentry/src/test/java/io/sentry/DiagnosticLoggerTest.kt @@ -4,6 +4,7 @@ import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.verify +import io.sentry.test.callMethod import kotlin.test.Test class DiagnosticLoggerTest { @@ -45,12 +46,12 @@ class DiagnosticLoggerTest { @Test fun `when debug is true, a call to log with null level does not throw`() { - fixture.getSut().log(null, expectedMessage) + fixture.getSut().callMethod("log", parameterTypes = arrayOf(SentryLevel::class.java, String::class.java, arrayOf()::class.java), null, expectedMessage, null) } @Test fun `when debug is true, a call to log with null level and throwable does not throw`() { - fixture.getSut().log(null, expectedMessage, expectedThrowable) + fixture.getSut().callMethod("log", parameterTypes = arrayOf(SentryLevel::class.java, String::class.java, Throwable::class.java), null, expectedMessage, expectedThrowable) } @Test From a951170bdc1d695bd55c85a1a3cc6abf87914633 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 13:43:53 +0200 Subject: [PATCH 32/40] Fix Scope#setTransaction nullability. --- sentry/src/main/java/io/sentry/Hub.java | 4 +++- sentry/src/main/java/io/sentry/Scope.java | 14 +++++++------- sentry/src/test/java/io/sentry/ScopeTest.kt | 9 ++++----- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index d998a5c259..be188416a7 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -314,8 +314,10 @@ public void setTransaction(final @Nullable String transaction) { .log( SentryLevel.WARNING, "Instance is disabled and this 'setTransaction' call is a no-op."); - } else { + } else if (transaction != null) { stack.peek().getScope().setTransaction(transaction); + } else { + options.getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); } } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 22e5abf05b..8d336a9c55 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -112,16 +112,16 @@ public void setLevel(final @Nullable SentryLevel level) { * * @param transaction the transaction */ - public void setTransaction(final @Nullable String transaction) { - final ITransaction tx = this.transaction; - if (tx != null) { - if (transaction != null) { + public void setTransaction(final @NotNull String transaction) { + if (transaction != null) { + final ITransaction tx = this.transaction; + if (tx != null) { tx.setName(transaction); - } else { - this.setTransaction((ITransaction) null); } + this.transactionName = transaction; + } else { + options.getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); } - this.transactionName = transaction; } /** diff --git a/sentry/src/test/java/io/sentry/ScopeTest.kt b/sentry/src/test/java/io/sentry/ScopeTest.kt index e6c1051c33..5430ab4d64 100644 --- a/sentry/src/test/java/io/sentry/ScopeTest.kt +++ b/sentry/src/test/java/io/sentry/ScopeTest.kt @@ -682,14 +682,13 @@ class ScopeTest { } @Test - fun `Scope setTransaction with null clears transaction`() { + fun `Scope setTransaction with null does not clear transaction`() { val scope = Scope(SentryOptions()) val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) scope.transaction = transaction - // todo: potentially we can improve the api here - scope.setTransaction(null as String?) - assertNull(scope.transaction) - assertNull(scope.transactionName) + scope.callMethod("setTransaction", String::class.java, null) + assertNotNull(scope.transaction) + assertNotNull(scope.transactionName) } @Test From 2bfb639eca0ba9a618e6a8e36ce31316ae392c39 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 13:44:56 +0200 Subject: [PATCH 33/40] Add explanation why NullAway is suppressed. --- sentry/src/main/java/io/sentry/SentryEnvelopeItem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java index 48715776f2..eb7301d1f4 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -48,6 +48,7 @@ public final class SentryEnvelopeItem { } // TODO: Should be a Stream + // dataFactory is a Callable which returns theoretically a nullable result. Our implementations always provide non-null values. @SuppressWarnings("NullAway") public @NotNull byte[] getData() throws Exception { if (data == null && dataFactory != null) { From 71eca422a34f1a32063cd4ad5f7850c4a28527b8 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 14:03:45 +0200 Subject: [PATCH 34/40] Polish. --- sentry/src/main/java/io/sentry/SentryEnvelopeItem.java | 3 ++- sentry/src/main/java/io/sentry/cache/CacheStrategy.java | 7 ++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java index eb7301d1f4..52818c02a0 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -48,7 +48,8 @@ public final class SentryEnvelopeItem { } // TODO: Should be a Stream - // dataFactory is a Callable which returns theoretically a nullable result. Our implementations always provide non-null values. + // dataFactory is a Callable which returns theoretically a nullable result. Our implementations + // always provide non-null values. @SuppressWarnings("NullAway") public @NotNull byte[] getData() throws Exception { if (data == null && dataFactory != null) { diff --git a/sentry/src/main/java/io/sentry/cache/CacheStrategy.java b/sentry/src/main/java/io/sentry/cache/CacheStrategy.java index e83e79011b..88a4ab2527 100644 --- a/sentry/src/main/java/io/sentry/cache/CacheStrategy.java +++ b/sentry/src/main/java/io/sentry/cache/CacheStrategy.java @@ -120,7 +120,7 @@ private void moveInitFlagIfNecessary( final @NotNull File currentFile, final @NotNull File[] notDeletedFiles) { final SentryEnvelope currentEnvelope = readEnvelope(currentFile); - if (currentEnvelope != null && !isValidEnvelope(currentEnvelope)) { + if (currentEnvelope == null || !isValidEnvelope(currentEnvelope)) { return; } @@ -219,10 +219,7 @@ private void moveInitFlagIfNecessary( return null; } - private @Nullable Session getFirstSession(final @Nullable SentryEnvelope envelope) { - if (envelope == null) { - return null; - } + private @Nullable Session getFirstSession(final @NotNull SentryEnvelope envelope) { for (final SentryEnvelopeItem item : envelope.getItems()) { if (!isSessionType(item)) { continue; From 3da98b160853b6fb0934d1839fd9be36478de9ae Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 15:19:34 +0200 Subject: [PATCH 35/40] Drop InvalidDsnException. --- .../android/core/SentryInitProviderTest.kt | 3 +- sentry/api/sentry.api | 8 ----- sentry/src/main/java/io/sentry/Dsn.java | 5 ++- .../sentry/exception/InvalidDsnException.java | 32 ------------------- sentry/src/test/java/io/sentry/DsnTest.kt | 8 ++--- sentry/src/test/java/io/sentry/HubTest.kt | 3 +- .../io/sentry/RequestDetailsResolverTest.kt | 3 +- .../test/java/io/sentry/SentryClientTest.kt | 4 +-- 8 files changed, 11 insertions(+), 55 deletions(-) delete mode 100644 sentry/src/main/java/io/sentry/exception/InvalidDsnException.java diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryInitProviderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryInitProviderTest.kt index 6c48a72492..ab75cd35eb 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryInitProviderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryInitProviderTest.kt @@ -6,7 +6,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.nhaarman.mockitokotlin2.mock import io.sentry.ILogger import io.sentry.Sentry -import io.sentry.exception.InvalidDsnException import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertFailsWith @@ -93,7 +92,7 @@ class SentryInitProviderTest { metaData.putString(ManifestMetadataReader.DSN, "invalid dsn") - assertFailsWith { sentryInitProvider.attachInfo(mockContext, providerInfo) } + assertFailsWith { sentryInitProvider.attachInfo(mockContext, providerInfo) } } @Test diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 13a015ef59..74c8e65e7c 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1255,14 +1255,6 @@ public final class io/sentry/exception/ExceptionMechanismException : java/lang/R public fun isSnapshot ()Z } -public final class io/sentry/exception/InvalidDsnException : java/lang/RuntimeException { - public fun (Ljava/lang/String;)V - public fun (Ljava/lang/String;Ljava/lang/String;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V - public fun (Ljava/lang/String;Ljava/lang/Throwable;)V - public fun getDsn ()Ljava/lang/String; -} - public final class io/sentry/exception/InvalidSentryTraceHeaderException : java/lang/Exception { public fun (Ljava/lang/String;)V public fun getSentryTraceHeader ()Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/Dsn.java b/sentry/src/main/java/io/sentry/Dsn.java index 04f713fa60..4eea9fe149 100644 --- a/sentry/src/main/java/io/sentry/Dsn.java +++ b/sentry/src/main/java/io/sentry/Dsn.java @@ -1,6 +1,5 @@ package io.sentry; -import io.sentry.exception.InvalidDsnException; import java.net.URI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -48,7 +47,7 @@ URI getSentryUri() { return sentryUri; } - Dsn(@Nullable String dsn) throws InvalidDsnException { + Dsn(@Nullable String dsn) throws IllegalArgumentException { try { URI uri = new URI(dsn).normalize(); String userInfo = uri.getUserInfo(); @@ -85,7 +84,7 @@ URI getSentryUri() { null, null); } catch (Exception e) { - throw new InvalidDsnException(dsn, e); + throw new IllegalArgumentException(e); } } } diff --git a/sentry/src/main/java/io/sentry/exception/InvalidDsnException.java b/sentry/src/main/java/io/sentry/exception/InvalidDsnException.java deleted file mode 100644 index 1597192b92..0000000000 --- a/sentry/src/main/java/io/sentry/exception/InvalidDsnException.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.sentry.exception; - -import org.jetbrains.annotations.Nullable; - -public final class InvalidDsnException extends RuntimeException { - private static final long serialVersionUID = 412945154259913013L; - private final @Nullable String dsn; - - public InvalidDsnException(final @Nullable String dsn) { - this.dsn = dsn; - } - - public InvalidDsnException(final @Nullable String dsn, final @Nullable String message) { - super(message); - this.dsn = dsn; - } - - public InvalidDsnException( - final @Nullable String dsn, final @Nullable String message, final @Nullable Throwable cause) { - super(message, cause); - this.dsn = dsn; - } - - public InvalidDsnException(final @Nullable String dsn, final @Nullable Throwable cause) { - super(cause); - this.dsn = dsn; - } - - public @Nullable String getDsn() { - return dsn; - } -} diff --git a/sentry/src/test/java/io/sentry/DsnTest.kt b/sentry/src/test/java/io/sentry/DsnTest.kt index 1076958ef0..5819d700d1 100644 --- a/sentry/src/test/java/io/sentry/DsnTest.kt +++ b/sentry/src/test/java/io/sentry/DsnTest.kt @@ -1,6 +1,6 @@ package io.sentry -import io.sentry.exception.InvalidDsnException +import java.lang.IllegalArgumentException import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -59,19 +59,19 @@ class DsnTest { @Test fun `when no project id exists, throws exception`() { - val ex = assertFailsWith { Dsn("http://key@host/") } + val ex = assertFailsWith { Dsn("http://key@host/") } assertEquals("java.lang.IllegalArgumentException: Invalid DSN: A Project Id is required.", ex.message) } @Test fun `when no key exists, throws exception`() { - val ex = assertFailsWith { Dsn("http://host/id") } + val ex = assertFailsWith { Dsn("http://host/id") } assertEquals("java.lang.IllegalArgumentException: Invalid DSN: No public key provided.", ex.message) } @Test fun `when only passing secret key, throws exception`() { - val ex = assertFailsWith { Dsn("https://:secret@host/path/id") } + val ex = assertFailsWith { Dsn("https://:secret@host/path/id") } assertEquals("java.lang.IllegalArgumentException: Invalid DSN: No public key provided.", ex.message) } diff --git a/sentry/src/test/java/io/sentry/HubTest.kt b/sentry/src/test/java/io/sentry/HubTest.kt index 3f1a43251f..32a3c20414 100644 --- a/sentry/src/test/java/io/sentry/HubTest.kt +++ b/sentry/src/test/java/io/sentry/HubTest.kt @@ -14,7 +14,6 @@ import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions import com.nhaarman.mockitokotlin2.whenever -import io.sentry.exception.InvalidDsnException import io.sentry.hints.SessionEndHint import io.sentry.hints.SessionStartHint import io.sentry.protocol.SentryId @@ -487,7 +486,7 @@ class HubTest { fun `when captureUserFeedback is called and client throws, don't crash`() { val (sut, mockClient) = getEnabledHub() - whenever(mockClient.captureUserFeedback(any())).doThrow(InvalidDsnException("")) + whenever(mockClient.captureUserFeedback(any())).doThrow(IllegalArgumentException("")) sut.captureUserFeedback(userFeedback) } diff --git a/sentry/src/test/java/io/sentry/RequestDetailsResolverTest.kt b/sentry/src/test/java/io/sentry/RequestDetailsResolverTest.kt index d2e9990621..ad4ee8892a 100644 --- a/sentry/src/test/java/io/sentry/RequestDetailsResolverTest.kt +++ b/sentry/src/test/java/io/sentry/RequestDetailsResolverTest.kt @@ -1,6 +1,5 @@ package io.sentry -import io.sentry.exception.InvalidDsnException import java.net.URL import kotlin.test.Test import kotlin.test.assertEquals @@ -11,7 +10,7 @@ class RequestDetailsResolverTest { @Test fun `When options doesn't have a valid DSN, it throws InvalidDsnException`() { - assertFailsWith { RequestDetailsResolver(SentryOptions()).resolve() } + assertFailsWith { RequestDetailsResolver(SentryOptions()).resolve() } } @Test diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 0cad53251d..ac49a48be4 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -12,7 +12,6 @@ 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 import io.sentry.hints.ApplyScopeData import io.sentry.hints.Cached @@ -31,6 +30,7 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.IOException import java.io.InputStreamReader +import java.lang.IllegalArgumentException import java.lang.IllegalStateException import java.lang.RuntimeException import java.nio.charset.Charset @@ -99,7 +99,7 @@ class SentryClientTest { fun `when dsn is an invalid string, client throws`() { fixture.sentryOptions.setTransportFactory(NoOpTransportFactory.getInstance()) fixture.sentryOptions.dsn = "invalid-dsn" - assertFailsWith { fixture.getSut() } + assertFailsWith { fixture.getSut() } } @Test From c764eb90f699902055603873354c7f26668929b9 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 15:21:25 +0200 Subject: [PATCH 36/40] Polish. --- sentry/src/main/java/io/sentry/transport/RateLimiter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sentry/src/main/java/io/sentry/transport/RateLimiter.java b/sentry/src/main/java/io/sentry/transport/RateLimiter.java index 428eebd71f..0ab3af7fa1 100644 --- a/sentry/src/main/java/io/sentry/transport/RateLimiter.java +++ b/sentry/src/main/java/io/sentry/transport/RateLimiter.java @@ -1,5 +1,6 @@ package io.sentry.transport; +import static io.sentry.SentryLevel.ERROR; import static io.sentry.SentryLevel.INFO; import io.sentry.ILogger; @@ -206,6 +207,8 @@ public void updateRetryAfterLimits( final String catItemCapitalized = StringUtils.capitalize(catItem); if (catItemCapitalized != null) { dataCategory = DataCategory.valueOf(catItemCapitalized); + } else { + logger.log(ERROR, "Couldn't capitalize: %s", catItem); } } catch (IllegalArgumentException e) { logger.log(INFO, e, "Unknown category: %s", catItem); From c5473c05178b28817f470e06c477860e87653d45 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 15:25:15 +0200 Subject: [PATCH 37/40] Polish `EnvelopeReader`. --- sentry/src/main/java/io/sentry/EnvelopeReader.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sentry/src/main/java/io/sentry/EnvelopeReader.java b/sentry/src/main/java/io/sentry/EnvelopeReader.java index e182de22cd..53381d8d1e 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeReader.java +++ b/sentry/src/main/java/io/sentry/EnvelopeReader.java @@ -87,16 +87,12 @@ SentryEnvelopeItemHeader.class, new SentryEnvelopeItemHeaderAdapter()) deserializeEnvelopeItemHeader( envelopeBytes, itemHeaderStartOffset, lineBreakIndex - itemHeaderStartOffset); - if (itemHeader == null) { - throw new IllegalArgumentException( - "Item header at index '" + items.size() + "' is null."); - } - if (itemHeader.getLength() <= 0) { + if (itemHeader == null || itemHeader.getLength() <= 0) { throw new IllegalArgumentException( "Item header at index '" + items.size() + "' has an invalid value: '" - + itemHeader.getLength() + + itemHeader + "'."); } From f64c1882ee6900d439ecf81b1ab1e7fe500ef2f3 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 15:56:18 +0200 Subject: [PATCH 38/40] Polish. --- .../main/java/io/sentry/EnvelopeReader.java | 6 +---- sentry/src/main/java/io/sentry/Hub.java | 24 ++++++++++--------- .../io/sentry/NoOpSentryExecutorService.java | 2 +- sentry/src/main/java/io/sentry/Sentry.java | 15 ++++++------ .../java/io/sentry/cache/EnvelopeCache.java | 2 +- .../test/java/io/sentry/SentryEnvelopeTest.kt | 2 +- 6 files changed, 24 insertions(+), 27 deletions(-) diff --git a/sentry/src/main/java/io/sentry/EnvelopeReader.java b/sentry/src/main/java/io/sentry/EnvelopeReader.java index 53381d8d1e..228b0dbae3 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeReader.java +++ b/sentry/src/main/java/io/sentry/EnvelopeReader.java @@ -89,11 +89,7 @@ SentryEnvelopeItemHeader.class, new SentryEnvelopeItemHeaderAdapter()) if (itemHeader == null || itemHeader.getLength() <= 0) { throw new IllegalArgumentException( - "Item header at index '" - + items.size() - + "' has an invalid value: '" - + itemHeader - + "'."); + "Item header at index '" + items.size() + "' is null or empty."); } payloadEndOffsetExclusive = lineBreakIndex + itemHeader.getLength() + 1; diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index be188416a7..0aee128d32 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -181,11 +181,13 @@ private void assignTraceContext(final @NotNull SentryEvent event) { if (event.getThrowable() != null) { final Pair pair = throwableToSpan.get(event.getThrowable()); if (pair != null) { - if (event.getContexts().getTrace() == null && pair.getFirst() != null) { - event.getContexts().setTrace(pair.getFirst().getSpanContext()); + final ISpan span = pair.getFirst(); + if (event.getContexts().getTrace() == null && span != null) { + event.getContexts().setTrace(span.getSpanContext()); } - if (event.getTransaction() == null && pair.getSecond() != null) { - event.setTransaction(pair.getSecond()); + final String transactionName = pair.getSecond(); + if (event.getTransaction() == null && transactionName != null) { + event.setTransaction(transactionName); } } } @@ -264,10 +266,7 @@ public void close() { ((Closeable) integration).close(); } } - final ISentryExecutorService executorService = options.getExecutorService(); - if (executorService != null) { - executorService.close(options.getShutdownTimeout()); - } + options.getExecutorService().close(options.getShutdownTimeout()); // Close the top-most client final StackItem item = stack.peek(); @@ -661,9 +660,12 @@ public void setSpanContext( @Nullable SpanContext getSpanContext(final @NotNull Throwable throwable) { Objects.requireNonNull(throwable, "throwable is required"); - final Pair span = this.throwableToSpan.get(throwable); - if (span != null && span.getFirst() != null) { - return span.getFirst().getSpanContext(); + final Pair pair = this.throwableToSpan.get(throwable); + if (pair != null) { + final ISpan span = pair.getFirst(); + if (span != null) { + return span.getSpanContext(); + } } return null; } diff --git a/sentry/src/main/java/io/sentry/NoOpSentryExecutorService.java b/sentry/src/main/java/io/sentry/NoOpSentryExecutorService.java index e8dec3ca1f..1f2231de3e 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryExecutorService.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryExecutorService.java @@ -9,7 +9,7 @@ final class NoOpSentryExecutorService implements ISentryExecutorService { private NoOpSentryExecutorService() {} - public static ISentryExecutorService getInstance() { + public static @NotNull ISentryExecutorService getInstance() { return instance; } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index e81160281a..1ce759995f 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -210,20 +210,19 @@ private static boolean initConfigurations(final @NotNull SentryOptions options) // eg release, distinctId, sentryClientName // this should be after setting serializers - if (options.getOutboxPath() != null - && options.getCacheDirPath() != null - && !options.getCacheDirPath().isEmpty()) { - final File cacheDir = new File(options.getCacheDirPath()); - cacheDir.mkdirs(); - + if (options.getOutboxPath() != null) { final File outboxDir = new File(options.getOutboxPath()); outboxDir.mkdirs(); - - options.setEnvelopeDiskCache(EnvelopeCache.create(options)); } else { logger.log(SentryLevel.INFO, "No outbox dir path is defined in options."); } + if (options.getCacheDirPath() != null && !options.getCacheDirPath().isEmpty()) { + final File cacheDir = new File(options.getCacheDirPath()); + cacheDir.mkdirs(); + options.setEnvelopeDiskCache(EnvelopeCache.create(options)); + } + return true; } diff --git a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java index 340759fa74..2aa062b5b3 100644 --- a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java @@ -69,7 +69,7 @@ public final class EnvelopeCache extends CacheStrategy implements IEnvelopeCache private EnvelopeCache( final @NotNull SentryOptions options, final @NotNull String cacheDirPath, - final @NotNull int cacheDirSize) { + final int cacheDirSize) { super(options, cacheDirPath, cacheDirSize); } diff --git a/sentry/src/test/java/io/sentry/SentryEnvelopeTest.kt b/sentry/src/test/java/io/sentry/SentryEnvelopeTest.kt index 227490dd40..c8351f5908 100644 --- a/sentry/src/test/java/io/sentry/SentryEnvelopeTest.kt +++ b/sentry/src/test/java/io/sentry/SentryEnvelopeTest.kt @@ -107,7 +107,7 @@ class SentryEnvelopeTest { {"content_type":"application/json","type":"event"} {}""".toInputStream() val exception = assertFailsWith { envelopeReader.read(stream) } - assertEquals("Item header at index '0' has an invalid value: '0'.", exception.message) + assertEquals("Item header at index '0' is null or empty.", exception.message) } @Test From 744425cfc76acf70ca039ada4dfcf9aff11e0e60 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Fri, 7 May 2021 16:07:06 +0200 Subject: [PATCH 39/40] Changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ccc7bdf7f..5758a88f31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Fix: Run event processors and enrich transactions with contexts (#1430) * Bump: sentry-native to 0.4.9 (#1431) * Fix: Set user on transaction in Spring & Spring Boot integrations (#1443) +* Ref: nullability annotations to Sentry module (#1439) # 4.4.0-alpha.2 From b577af56465ab273101483355fc678d66fceacbd Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Mon, 10 May 2021 14:37:45 +0200 Subject: [PATCH 40/40] release on Session is not null --- sentry/src/main/java/io/sentry/Hub.java | 17 +- sentry/src/main/java/io/sentry/Scope.java | 24 ++- sentry/src/main/java/io/sentry/Session.java | 8 +- .../main/java/io/sentry/SessionAdapter.java | 4 +- sentry/src/test/java/io/sentry/HubTest.kt | 7 + sentry/src/test/java/io/sentry/ScopeTest.kt | 191 +++++++++++------- .../test/java/io/sentry/SentryClientTest.kt | 111 +++++----- 7 files changed, 224 insertions(+), 138 deletions(-) diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index 0aee128d32..d0e6c2f310 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -226,15 +226,18 @@ public void startSession() { } else { final StackItem item = this.stack.peek(); final Scope.SessionPair pair = item.getScope().startSession(); + if (pair != null) { + // TODO: add helper overload `captureSessions` to pass a list of sessions and submit a + // single envelope + // Or create the envelope here with both items and call `captureEnvelope` + if (pair.getPrevious() != null) { + item.getClient().captureSession(pair.getPrevious(), new SessionEndHint()); + } - // TODO: add helper overload `captureSessions` to pass a list of sessions and submit a - // single envelope - // Or create the envelope here with both items and call `captureEnvelope` - if (pair.getPrevious() != null) { - item.getClient().captureSession(pair.getPrevious(), new SessionEndHint()); + item.getClient().captureSession(pair.getCurrent(), new SessionStartHint()); + } else { + options.getLogger().log(SentryLevel.WARNING, "Session could not be started."); } - - item.getClient().captureSession(pair.getCurrent(), new SessionStartHint()); } } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 8d336a9c55..151a7e6b44 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -626,10 +626,10 @@ interface IWithSession { * * @return the SessionPair with the previous closed session if exists and the current session */ - @NotNull + @Nullable SessionPair startSession() { Session previousSession; - SessionPair pair; + SessionPair pair = null; synchronized (sessionLock) { if (session != null) { // Assumes session will NOT flush itself (Not passing any hub to it) @@ -637,12 +637,20 @@ SessionPair startSession() { } previousSession = session; - session = - new Session( - options.getDistinctId(), user, options.getEnvironment(), options.getRelease()); - - final Session previousClone = previousSession != null ? previousSession.clone() : null; - pair = new SessionPair(session.clone(), previousClone); + if (options.getRelease() != null) { + session = + new Session( + options.getDistinctId(), user, options.getEnvironment(), options.getRelease()); + + final Session previousClone = previousSession != null ? previousSession.clone() : null; + pair = new SessionPair(session.clone(), previousClone); + } else { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Release is not set on SentryOptions. Session could not be started"); + } } return pair; } diff --git a/sentry/src/main/java/io/sentry/Session.java b/sentry/src/main/java/io/sentry/Session.java index 10785b74c8..06710e1787 100644 --- a/sentry/src/main/java/io/sentry/Session.java +++ b/sentry/src/main/java/io/sentry/Session.java @@ -55,7 +55,7 @@ public enum State { private final @Nullable String environment; /** the App's release */ - private final @Nullable String release; + private final @NotNull String release; /** The session lock, ops should be atomic */ private final @NotNull Object sessionLock = new Object(); @@ -73,7 +73,7 @@ public Session( final @Nullable String ipAddress, final @Nullable String userAgent, final @Nullable String environment, - final @Nullable String release) { + final @NotNull String release) { this.status = status; this.started = started; this.timestamp = timestamp; @@ -93,7 +93,7 @@ public Session( @Nullable String distinctId, final @Nullable User user, final @Nullable String environment, - final @Nullable String release) { + final @NotNull String release) { this( State.Ok, DateUtils.getCurrentDateTime(), @@ -138,7 +138,7 @@ public Session( return environment; } - public @Nullable String getRelease() { + public @NotNull String getRelease() { return release; } diff --git a/sentry/src/main/java/io/sentry/SessionAdapter.java b/sentry/src/main/java/io/sentry/SessionAdapter.java index 2f12d360a1..3d2d49f16a 100644 --- a/sentry/src/main/java/io/sentry/SessionAdapter.java +++ b/sentry/src/main/java/io/sentry/SessionAdapter.java @@ -73,9 +73,7 @@ public void write(JsonWriter writer, Session value) throws IOException { boolean hasInitAttrs = false; hasInitAttrs = initAttrs(writer, hasInitAttrs); - if (value.getRelease() != null) { - writer.name("release").value(value.getRelease()); - } + writer.name("release").value(value.getRelease()); if (value.getEnvironment() != null) { hasInitAttrs = initAttrs(writer, hasInitAttrs); diff --git a/sentry/src/test/java/io/sentry/HubTest.kt b/sentry/src/test/java/io/sentry/HubTest.kt index 32a3c20414..e016ce0cc6 100644 --- a/sentry/src/test/java/io/sentry/HubTest.kt +++ b/sentry/src/test/java/io/sentry/HubTest.kt @@ -889,6 +889,7 @@ class HubTest { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" options.setSerializer(mock()) val sut = Hub(options) val mockClient = mock() @@ -904,6 +905,7 @@ class HubTest { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" options.setSerializer(mock()) val sut = Hub(options) val mockClient = mock() @@ -918,6 +920,7 @@ class HubTest { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" options.setSerializer(mock()) val sut = Hub(options) val mockClient = mock() @@ -936,6 +939,7 @@ class HubTest { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" options.setSerializer(mock()) val sut = Hub(options) val mockClient = mock() @@ -951,6 +955,7 @@ class HubTest { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" options.setSerializer(mock()) val sut = Hub(options) val mockClient = mock() @@ -965,6 +970,7 @@ class HubTest { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" options.setSerializer(mock()) val sut = Hub(options) val mockClient = mock() @@ -981,6 +987,7 @@ class HubTest { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" options.setSerializer(mock()) val sut = Hub(options) val mockClient = mock() diff --git a/sentry/src/test/java/io/sentry/ScopeTest.kt b/sentry/src/test/java/io/sentry/ScopeTest.kt index 5430ab4d64..8ad4318442 100644 --- a/sentry/src/test/java/io/sentry/ScopeTest.kt +++ b/sentry/src/test/java/io/sentry/ScopeTest.kt @@ -305,15 +305,19 @@ class ScopeTest { scope.user = user val sessionPair = scope.startSession() - assertNotNull(sessionPair.current) - assertEquals("rel", sessionPair.current.release) - assertEquals("env", sessionPair.current.environment) - assertEquals("123", sessionPair.current.distinctId) + assertNotNull(sessionPair) { + assertNotNull(it.current) + assertEquals("rel", it.current.release) + assertEquals("env", it.current.environment) + assertEquals("123", it.current.distinctId) + } } @Test fun `Scope ends a session and returns it if theres one`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) @@ -324,7 +328,9 @@ class ScopeTest { @Test fun `Scope ends a session and returns null if none exist`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) val session = scope.endSession() @@ -333,7 +339,9 @@ class ScopeTest { @Test fun `withSession returns a callback with the current Session`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) scope.startSession() @@ -344,7 +352,9 @@ class ScopeTest { @Test fun `withSession returns a callback with a null session if theres none`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) scope.withSession { @@ -354,130 +364,171 @@ class ScopeTest { @Test fun `Scope clones the start and end session objects`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) val sessionPair = scope.startSession() - val endSession = scope.endSession()!! + assertNotNull(sessionPair) { + val endSession = scope.endSession()!! - assertNotSame(sessionPair.current, endSession) + assertNotSame(it.current, endSession) + } } @Test - fun `Scope sets init to null when mutating a session`() { + fun `when release is not set, startSession returns null`() { val options = SentryOptions() val scope = Scope(options) + assertNull(scope.startSession()) + } - val start = scope.startSession().current - - scope.withSession { - it!!.update(null, null, false) + @Test + fun `Scope sets init to null when mutating a session`() { + val options = SentryOptions().apply { + release = "0.0.1" } + val scope = Scope(options) + + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val start = it.current + + scope.withSession { session -> + session!!.update(null, null, false) + } - val end = scope.endSession()!! + val end = scope.endSession()!! - assertTrue(start.init!!) - assertNull(end.init) + assertTrue(start.init!!) + assertNull(end.init) + } } @Test fun `Scope increases session error count when capturing an error`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) - val start = scope.startSession().current + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val start = it.current - scope.withSession { - it!!.update(null, null, true) - } + scope.withSession { session -> + session!!.update(null, null, true) + } - val end = scope.endSession()!! + val end = scope.endSession()!! - assertEquals(0, start.errorCount()) - assertEquals(1, end.errorCount()) + assertEquals(0, start.errorCount()) + assertEquals(1, end.errorCount()) + } } @Test fun `Scope sets status when capturing a fatal error`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) - val start = scope.startSession().current - - scope.withSession { - it!!.update(Session.State.Crashed, null, true) - } + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val start = it.current + scope.withSession { session -> + session!!.update(Session.State.Crashed, null, true) + } - val end = scope.endSession()!! + val end = scope.endSession()!! - assertEquals(Session.State.Ok, start.status) - assertEquals(Session.State.Crashed, end.status) + assertEquals(Session.State.Ok, start.status) + assertEquals(Session.State.Crashed, end.status) + } } @Test fun `Scope sets user agent when capturing an error`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) - val start = scope.startSession().current - - scope.withSession { - it!!.update(null, "jamesBond", true) - } + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val start = it.current + scope.withSession { session -> + session!!.update(null, "jamesBond", true) + } - val end = scope.endSession()!! + val end = scope.endSession()!! - assertNull(start.userAgent) - assertEquals("jamesBond", end.userAgent) + assertNull(start.userAgent) + assertEquals("jamesBond", end.userAgent) + } } @Test fun `Scope sets timestamp when capturing an error`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) - val start = scope.startSession().current + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val start = it.current + scope.withSession { session -> + session!!.update(null, null, true) + } + val end = scope.endSession()!! - scope.withSession { - it!!.update(null, null, true) + assertNotSame(end.timestamp!!, start.timestamp) } - val end = scope.endSession()!! - - assertNotSame(end.timestamp!!, start.timestamp) } @Test fun `Scope increases sequence when capturing an error`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) - val start = scope.startSession().current - - scope.withSession { - it!!.update(null, null, true) - } + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val start = it.current + scope.withSession { session -> + session!!.update(null, null, true) + } - val end = scope.endSession()!! + val end = scope.endSession()!! - assertNull(start.sequence) - assertTrue(end.sequence!! > 0) + assertNull(start.sequence) + assertTrue(end.sequence!! > 0) + } } @Test fun `Scope sets duration when ending a session`() { - val options = SentryOptions() + val options = SentryOptions().apply { + release = "0.0.1" + } val scope = Scope(options) - val start = scope.startSession().current - - scope.withSession { - it!!.update(null, null, true) - } + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val start = it.current + scope.withSession { session -> + session!!.update(null, null, true) + } - val end = scope.endSession()!! + val end = scope.endSession()!! - assertNull(start.duration) - assertNotNull(end.duration) + assertNull(start.duration) + assertNotNull(end.duration) + } } @Test diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index ac49a48be4..453671b10f 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -65,6 +65,7 @@ class SentryClientTest { setLogger(mock()) maxAttachmentSize = this@Fixture.maxAttachmentSize setTransportFactory(factory) + release = "0.0.1" } val hub = mock() @@ -648,7 +649,8 @@ class SentryClientTest { @Test fun `When event is non handled, mark session as Crashed`() { val scope = Scope(fixture.sentryOptions) - scope.startSession().current + scope.startSession() + val event = SentryEvent().apply { exceptions = createNonHandledException() } @@ -661,17 +663,20 @@ class SentryClientTest { @Test fun `When event is handled, keep level as it is`() { val scope = Scope(fixture.sentryOptions) - val session = scope.startSession().current - val level = session.status - val event = SentryEvent() - fixture.getSut().updateSessionData(event, null, scope) - assertEquals(level, session.status) + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val session = it.current + val level = session.status + val event = SentryEvent() + fixture.getSut().updateSessionData(event, null, scope) + assertEquals(level, session.status) + } } @Test fun `When event is non handled, increase errorCount`() { val scope = Scope(fixture.sentryOptions) - scope.startSession().current + scope.startSession() val event = SentryEvent().apply { exceptions = createNonHandledException() } @@ -684,7 +689,7 @@ class SentryClientTest { @Test fun `When event is Errored, increase errorCount`() { val scope = Scope(fixture.sentryOptions) - scope.startSession().current + scope.startSession() val exceptions = mutableListOf() exceptions.add(SentryException()) val event = SentryEvent().apply { @@ -699,40 +704,48 @@ class SentryClientTest { @Test fun `When event is handled and not errored, do not increase errorsCount`() { val scope = Scope(fixture.sentryOptions) - val session = scope.startSession().current - val errorCount = session.errorCount() - val event = SentryEvent() - fixture.getSut().updateSessionData(event, null, scope) - assertEquals(errorCount, session.errorCount()) + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val session = it.current + val errorCount = session.errorCount() + val event = SentryEvent() + fixture.getSut().updateSessionData(event, null, scope) + assertEquals(errorCount, session.errorCount()) + } } @Test fun `When event has userAgent, set it into session`() { val scope = Scope(fixture.sentryOptions) - scope.startSession().current - val event = SentryEvent().apply { - request = Request().apply { - headers = mutableMapOf("user-agent" to "jamesBond") + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val event = SentryEvent().apply { + request = Request().apply { + headers = mutableMapOf("user-agent" to "jamesBond") + } + } + fixture.getSut().updateSessionData(event, null, scope) + scope.withSession { + assertEquals("jamesBond", it!!.userAgent) } - } - fixture.getSut().updateSessionData(event, null, scope) - scope.withSession { - assertEquals("jamesBond", it!!.userAgent) } } @Test fun `When event has no userAgent, keep as it is`() { val scope = Scope(fixture.sentryOptions) - val session = scope.startSession().current - val userAgent = session.userAgent - val event = SentryEvent().apply { - request = Request().apply { - headers = mutableMapOf() + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + val session = it.current + val userAgent = session.userAgent + val event = SentryEvent().apply { + request = Request().apply { + headers = mutableMapOf() + } } + fixture.getSut().updateSessionData(event, null, scope) + assertEquals(userAgent, session.userAgent) } - fixture.getSut().updateSessionData(event, null, scope) - assertEquals(userAgent, session.userAgent) } @Test @@ -754,11 +767,13 @@ class SentryClientTest { exceptions = createNonHandledException() } val scope = Scope(fixture.sentryOptions) - scope.startSession().current - sut.captureEvent(event, scope, null) - scope.withSession { - assertEquals(Session.State.Crashed, it!!.status) - assertEquals(1, it.errorCount()) + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + sut.captureEvent(event, scope, null) + scope.withSession { + assertEquals(Session.State.Crashed, it!!.status) + assertEquals(1, it.errorCount()) + } } } @@ -768,13 +783,15 @@ class SentryClientTest { val scope = Scope(fixture.sentryOptions) scope.setContexts("key", "abc") - scope.startSession().current - sut.captureEvent(SentryEvent(), scope, null) - verify(fixture.transport).send(check { - val event = getEventFromData(it.items.first().data) - val map = event.contexts["key"] as Map<*, *> - assertEquals("abc", map["value"]) - }, anyOrNull()) + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + sut.captureEvent(SentryEvent(), scope, null) + verify(fixture.transport).send(check { + val event = getEventFromData(it.items.first().data) + val map = event.contexts["key"] as Map<*, *> + assertEquals("abc", map["value"]) + }, anyOrNull()) + } } @Test @@ -785,12 +802,14 @@ class SentryClientTest { event.contexts["key"] = "event value" val scope = Scope(fixture.sentryOptions) scope.setContexts("key", "scope value") - scope.startSession().current - sut.captureEvent(event, scope, null) - verify(fixture.transport).send(check { - val eventFromData = getEventFromData(it.items.first().data) - assertEquals("event value", eventFromData.contexts["key"]) - }, anyOrNull()) + val sessionPair = scope.startSession() + assertNotNull(sessionPair) { + sut.captureEvent(event, scope, null) + verify(fixture.transport).send(check { + val eventFromData = getEventFromData(it.items.first().data) + assertEquals("event value", eventFromData.contexts["key"]) + }, anyOrNull()) + } } @Test