fileNameMap = new WeakHashMap<>();
- public SessionCache(final @NotNull SentryOptions options) {
- super(options, options.getSessionsPath(), options.getSessionsDirSize());
+ public EnvelopeCache(final @NotNull SentryOptions options) {
+ super(options, options.getCacheDirPath(), options.getCacheDirSize());
}
@Override
@@ -133,12 +132,7 @@ public void store(final @NotNull SentryEnvelope envelope, final @Nullable Object
}
updateCurrentSession(currentSessionFile, envelope);
}
-
- if (hint instanceof SessionUpdate) {
- updateCurrentSession(currentSessionFile, envelope);
- return;
- }
-
+ // TODO: problem we need to update the current session file
final File envelopeFile = getEnvelopeFile(envelope);
if (envelopeFile.exists()) {
options
diff --git a/sentry-core/src/main/java/io/sentry/core/cache/IEventCache.java b/sentry-core/src/main/java/io/sentry/core/cache/IEventCache.java
deleted file mode 100644
index 6a29ebe0a..000000000
--- a/sentry-core/src/main/java/io/sentry/core/cache/IEventCache.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package io.sentry.core.cache;
-
-import io.sentry.core.SentryEvent;
-
-/**
- * Implementations of this interface are used as a kind of persistent storage for events that wait
- * to be sent to the Sentry server.
- *
- * Note that this interface doesn't handle the situation of resending the stored events after a
- * crash. While that is surely one of the main usecases for the persistent storage of events, the
- * re-initialization is out of scope of the event transport logic.
- */
-public interface IEventCache extends Iterable {
-
- /**
- * Stores the event so that it can be sent later.
- *
- * @param event the event to store
- */
- void store(SentryEvent event);
-
- /**
- * Discards the event from the storage. This means that the event has been successfully sent. Note
- * that this MUST NOT fail on events that haven't been stored before (i.e. this method is called
- * even for events that has been sent on the first attempt).
- *
- * @param event the event to discard from storage
- */
- void discard(SentryEvent event);
-}
diff --git a/sentry-core/src/main/java/io/sentry/core/hints/SessionUpdate.java b/sentry-core/src/main/java/io/sentry/core/hints/SessionUpdate.java
deleted file mode 100644
index 85d843ed1..000000000
--- a/sentry-core/src/main/java/io/sentry/core/hints/SessionUpdate.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.sentry.core.hints;
-
-/** Hint that shows this is a session update envelope */
-public interface SessionUpdate {}
diff --git a/sentry-core/src/main/java/io/sentry/core/hints/SessionUpdateHint.java b/sentry-core/src/main/java/io/sentry/core/hints/SessionUpdateHint.java
deleted file mode 100644
index fca36f278..000000000
--- a/sentry-core/src/main/java/io/sentry/core/hints/SessionUpdateHint.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package io.sentry.core.hints;
-
-public final class SessionUpdateHint implements SessionUpdate {}
diff --git a/sentry-core/src/main/java/io/sentry/core/transport/AsyncConnection.java b/sentry-core/src/main/java/io/sentry/core/transport/AsyncConnection.java
index a351597d8..5e7b484b9 100644
--- a/sentry-core/src/main/java/io/sentry/core/transport/AsyncConnection.java
+++ b/sentry-core/src/main/java/io/sentry/core/transport/AsyncConnection.java
@@ -3,16 +3,12 @@
import io.sentry.core.ILogger;
import io.sentry.core.SentryEnvelope;
import io.sentry.core.SentryEnvelopeItem;
-import io.sentry.core.SentryEvent;
-import io.sentry.core.SentryItemType;
import io.sentry.core.SentryLevel;
import io.sentry.core.SentryOptions;
import io.sentry.core.cache.IEnvelopeCache;
-import io.sentry.core.cache.IEventCache;
import io.sentry.core.hints.Cached;
import io.sentry.core.hints.DiskFlushNotification;
import io.sentry.core.hints.Retryable;
-import io.sentry.core.hints.SessionUpdate;
import io.sentry.core.hints.SubmissionResult;
import io.sentry.core.util.LogUtils;
import io.sentry.core.util.Objects;
@@ -35,23 +31,20 @@ public final class AsyncConnection implements Closeable, Connection {
private final @NotNull ITransport transport;
private final @NotNull ITransportGate transportGate;
private final @NotNull ExecutorService executor;
- private final @NotNull IEventCache eventCache;
- private final @NotNull IEnvelopeCache sessionCache;
+ private final @NotNull IEnvelopeCache envelopeCache;
private final @NotNull SentryOptions options;
public AsyncConnection(
final ITransport transport,
final ITransportGate transportGate,
- final IEventCache eventCache,
- final IEnvelopeCache sessionCache,
+ final IEnvelopeCache envelopeCache,
final int maxQueueSize,
final SentryOptions options) {
this(
transport,
transportGate,
- eventCache,
- sessionCache,
- initExecutor(maxQueueSize, eventCache, sessionCache, options.getLogger()),
+ envelopeCache,
+ initExecutor(maxQueueSize, envelopeCache, options.getLogger()),
options);
}
@@ -59,44 +52,31 @@ public AsyncConnection(
AsyncConnection(
final @NotNull ITransport transport,
final @NotNull ITransportGate transportGate,
- final @NotNull IEventCache eventCache,
- final @NotNull IEnvelopeCache sessionCache,
+ final @NotNull IEnvelopeCache envelopeCache,
final @NotNull ExecutorService executorService,
final @NotNull SentryOptions options) {
this.transport = transport;
this.transportGate = transportGate;
- this.eventCache = eventCache;
- this.sessionCache = sessionCache;
+ this.envelopeCache = envelopeCache;
this.options = options;
this.executor = executorService;
}
private static QueuedThreadPoolExecutor initExecutor(
final int maxQueueSize,
- final @NotNull IEventCache eventCache,
- final @NotNull IEnvelopeCache sessionCache,
+ final @NotNull IEnvelopeCache envelopeCache,
final @NotNull ILogger logger) {
final RejectedExecutionHandler storeEvents =
(r, executor) -> {
- if (r instanceof EventSender) {
- final EventSender eventSender = (EventSender) r;
+ if (r instanceof EnvelopeSender) {
+ final EnvelopeSender envelopeSender = (EnvelopeSender) r;
- if (!(eventSender.hint instanceof Cached)) {
- eventCache.store(eventSender.event);
+ if (!(envelopeSender.hint instanceof Cached)) {
+ envelopeCache.store(envelopeSender.envelope, envelopeSender.hint);
}
- markHintWhenSendingFailed(eventSender.hint, true);
- logger.log(SentryLevel.WARNING, "Event rejected: %s", eventSender.event.getEventId());
- }
- if (r instanceof SessionSender) {
- final SessionSender sessionSender = (SessionSender) r;
-
- if (!(sessionSender.hint instanceof Cached)) {
- sessionCache.store(sessionSender.envelope, sessionSender.hint);
- }
-
- markHintWhenSendingFailed(sessionSender.hint, true);
+ markHintWhenSendingFailed(envelopeSender.hint, true);
logger.log(SentryLevel.WARNING, "Envelope rejected");
}
};
@@ -120,42 +100,12 @@ private static void markHintWhenSendingFailed(final @Nullable Object hint, final
}
}
- /**
- * Tries to send the event to the Sentry server.
- *
- * @param event the event to send
- * @throws IOException on error
- */
- @SuppressWarnings("FutureReturnValueIgnored")
- @Override
- public void send(final @NotNull SentryEvent event, final @Nullable Object hint)
- throws IOException {
- IEventCache currentEventCache = eventCache;
- boolean cached = false;
- if (hint instanceof Cached) {
- currentEventCache = NoOpEventCache.getInstance();
- cached = true;
- options.getLogger().log(SentryLevel.DEBUG, "Captured SentryEvent is already cached");
- }
-
- // no reason to continue
- if (transport.isRetryAfter(SentryItemType.Event.getItemType())) {
- if (cached) {
- eventCache.discard(event);
- }
- markHintWhenSendingFailed(hint, false);
- return;
- }
-
- executor.submit(new EventSender(event, hint, currentEventCache));
- }
-
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void send(@NotNull SentryEnvelope envelope, final @Nullable Object hint)
throws IOException {
// For now no caching on envelopes
- IEnvelopeCache currentEnvelopeCache = sessionCache;
+ IEnvelopeCache currentEnvelopeCache = envelopeCache;
boolean cached = false;
if (hint instanceof Cached) {
currentEnvelopeCache = NoOpEnvelopeCache.getInstance();
@@ -192,7 +142,7 @@ public void send(@NotNull SentryEnvelope envelope, final @Nullable Object hint)
// no reason to continue
if (toSend.isEmpty()) {
if (cached) {
- sessionCache.discard(envelope);
+ envelopeCache.discard(envelope);
}
options.getLogger().log(SentryLevel.INFO, "Envelope discarded due all items rate limited.");
@@ -203,7 +153,7 @@ public void send(@NotNull SentryEnvelope envelope, final @Nullable Object hint)
envelope = new SentryEnvelope(envelope.getHeader(), toSend);
}
- executor.submit(new SessionSender(envelope, hint, currentEnvelopeCache));
+ executor.submit(new EnvelopeSender(envelope, hint, currentEnvelopeCache));
}
@Override
@@ -240,103 +190,19 @@ private static final class AsyncConnectionThreadFactory implements ThreadFactory
}
}
- private final class EventSender implements Runnable {
- private final SentryEvent event;
- private final Object hint;
- private final IEventCache eventCache;
- private final TransportResult failedResult = TransportResult.error();
-
- EventSender(
- final @NotNull SentryEvent event,
- final @Nullable Object hint,
- final @NotNull IEventCache eventCache) {
- this.event = event;
- this.hint = hint;
- this.eventCache = eventCache;
- }
-
- @Override
- public void run() {
- TransportResult result = this.failedResult;
- try {
- result = flush();
- } catch (Exception e) {
- options
- .getLogger()
- .log(SentryLevel.ERROR, e, "Event submission failed: %s", event.getEventId());
- throw e;
- } finally {
- if (hint instanceof SubmissionResult) {
- options
- .getLogger()
- .log(SentryLevel.DEBUG, "Marking event submission result: %s", result.isSuccess());
- ((SubmissionResult) hint).setResult(result.isSuccess());
- }
- }
- }
-
- private @NotNull TransportResult flush() {
- TransportResult result = this.failedResult;
- eventCache.store(event);
-
- if (hint instanceof DiskFlushNotification) {
- ((DiskFlushNotification) hint).markFlushed();
- options
- .getLogger()
- .log(SentryLevel.DEBUG, "Disk flush event fired: %s", event.getEventId());
- }
-
- if (transportGate.isConnected()) {
- try {
- result =
- transport.send(
- SentryEnvelope.fromEvent(
- options.getSerializer(), event, options.getSdkVersion()));
- if (result.isSuccess()) {
- eventCache.discard(event);
- } else {
- final String message =
- "The transport failed to send the event with response code "
- + result.getResponseCode();
-
- options.getLogger().log(SentryLevel.ERROR, message);
-
- throw new IllegalStateException(message);
- }
- } catch (IOException e) {
- // Failure due to IO is allowed to retry the event
- if (hint instanceof Retryable) {
- ((Retryable) hint).setRetry(true);
- } else {
- LogUtils.logIfNotRetryable(options.getLogger(), hint);
- }
- throw new IllegalStateException("Sending the event failed.", e);
- }
- } else {
- // If transportGate is blocking from sending, allowed to retry
- if (hint instanceof Retryable) {
- ((Retryable) hint).setRetry(true);
- } else {
- LogUtils.logIfNotRetryable(options.getLogger(), hint);
- }
- }
- return result;
- }
- }
-
- private final class SessionSender implements Runnable {
+ private final class EnvelopeSender implements Runnable {
private final @NotNull SentryEnvelope envelope;
private final @Nullable Object hint;
- private final @NotNull IEnvelopeCache sessionCache;
+ private final @NotNull IEnvelopeCache envelopeCache;
private final TransportResult failedResult = TransportResult.error();
- SessionSender(
+ EnvelopeSender(
final @NotNull SentryEnvelope envelope,
final @Nullable Object hint,
- final @NotNull IEnvelopeCache sessionCache) {
+ final @NotNull IEnvelopeCache envelopeCache) {
this.envelope = Objects.requireNonNull(envelope, "Envelope is required.");
this.hint = hint;
- this.sessionCache = Objects.requireNonNull(sessionCache, "SessionCache is required.");
+ this.envelopeCache = Objects.requireNonNull(envelopeCache, "EnvelopeCache is required.");
}
@Override
@@ -361,21 +227,18 @@ public void run() {
private @NotNull TransportResult flush() {
TransportResult result = this.failedResult;
- sessionCache.store(envelope, hint);
+ envelopeCache.store(envelope, hint);
- // we only flush a session update to the disk, but not to the network
- if (hint instanceof SessionUpdate) {
- options
- .getLogger()
- .log(SentryLevel.DEBUG, "SessionUpdate event, leaving after event being cached.");
- return TransportResult.success();
+ if (hint instanceof DiskFlushNotification) {
+ ((DiskFlushNotification) hint).markFlushed();
+ options.getLogger().log(SentryLevel.DEBUG, "Disk flush envelope fired");
}
if (transportGate.isConnected()) {
try {
result = transport.send(envelope);
if (result.isSuccess()) {
- sessionCache.discard(envelope);
+ envelopeCache.discard(envelope);
} else {
final String message =
"The transport failed to send the envelope with response code "
diff --git a/sentry-core/src/main/java/io/sentry/core/transport/Connection.java b/sentry-core/src/main/java/io/sentry/core/transport/Connection.java
index 02a5da2ae..d925c1b4c 100644
--- a/sentry-core/src/main/java/io/sentry/core/transport/Connection.java
+++ b/sentry-core/src/main/java/io/sentry/core/transport/Connection.java
@@ -1,17 +1,10 @@
package io.sentry.core.transport;
import io.sentry.core.SentryEnvelope;
-import io.sentry.core.SentryEvent;
import java.io.IOException;
import org.jetbrains.annotations.Nullable;
public interface Connection {
- void send(SentryEvent event, @Nullable Object hint) throws IOException;
-
- default void send(SentryEvent event) throws IOException {
- send(event, null);
- }
-
void send(SentryEnvelope event, @Nullable Object hint) throws IOException;
default void send(SentryEnvelope envelope) throws IOException {
diff --git a/sentry-core/src/main/java/io/sentry/core/transport/NoOpEventCache.java b/sentry-core/src/main/java/io/sentry/core/transport/NoOpEventCache.java
deleted file mode 100644
index 83a5cd265..000000000
--- a/sentry-core/src/main/java/io/sentry/core/transport/NoOpEventCache.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package io.sentry.core.transport;
-
-import io.sentry.core.SentryEvent;
-import io.sentry.core.cache.IEventCache;
-import java.util.ArrayList;
-import java.util.Iterator;
-import org.jetbrains.annotations.NotNull;
-
-public final class NoOpEventCache implements IEventCache {
- private static final NoOpEventCache instance = new NoOpEventCache();
-
- public static NoOpEventCache getInstance() {
- return instance;
- }
-
- private NoOpEventCache() {}
-
- @Override
- public void store(SentryEvent event) {}
-
- @Override
- public void discard(SentryEvent event) {}
-
- @Override
- public @NotNull Iterator iterator() {
- return new ArrayList(0).iterator();
- }
-}
diff --git a/sentry-core/src/test/java/io/sentry/core/DirectoryProcessorTest.kt b/sentry-core/src/test/java/io/sentry/core/DirectoryProcessorTest.kt
index 231180698..8039ca6ae 100644
--- a/sentry-core/src/test/java/io/sentry/core/DirectoryProcessorTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/DirectoryProcessorTest.kt
@@ -32,8 +32,8 @@ class DirectoryProcessorTest {
options.setLogger(logger)
}
- fun getSut(): EnvelopeSender {
- return EnvelopeSender(hub, envelopeReader, serializer, logger, 15000)
+ fun getSut(): OutboxSender {
+ return OutboxSender(hub, envelopeReader, serializer, logger, 15000)
}
}
@@ -55,11 +55,17 @@ class DirectoryProcessorTest {
fun `process directory folder has a non ApplyScopeData hint`() {
val path = getTempEnvelope("envelope-event-attachment.txt")
assertTrue(File(path).exists()) // sanity check
- val session = createSession()
- whenever(fixture.envelopeReader.read(any())).thenReturn(SentryEnvelope.fromSession(fixture.serializer, session, null))
- whenever(fixture.serializer.deserializeSession(any())).thenReturn(session)
+// val session = createSession()
+// whenever(fixture.envelopeReader.read(any())).thenReturn(SentryEnvelope.fromSession(fixture.serializer, session, null))
+// whenever(fixture.serializer.deserializeSession(any())).thenReturn(session)
+ val event = SentryEvent()
+ val envelope = SentryEnvelope.fromEvent(fixture.serializer, event, null)
+
+ whenever(fixture.envelopeReader.read(any())).thenReturn(envelope)
+ whenever(fixture.serializer.deserializeEvent(any())).thenReturn(event)
+
fixture.getSut().processDirectory(file)
- verify(fixture.hub).captureEnvelope(any(), argWhere { it !is ApplyScopeData })
+ verify(fixture.hub).captureEvent(any(), argWhere { it !is ApplyScopeData })
}
@Test
diff --git a/sentry-core/src/test/java/io/sentry/core/EnvelopeSenderTest.kt b/sentry-core/src/test/java/io/sentry/core/EnvelopeSenderTest.kt
index 7f2817375..7fb998994 100644
--- a/sentry-core/src/test/java/io/sentry/core/EnvelopeSenderTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/EnvelopeSenderTest.kt
@@ -1,187 +1,115 @@
package io.sentry.core
import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.argWhere
+import com.nhaarman.mockitokotlin2.doThrow
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.never
import com.nhaarman.mockitokotlin2.verify
+import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import com.nhaarman.mockitokotlin2.whenever
-import io.sentry.core.cache.SessionCache
+import io.sentry.core.cache.EnvelopeCache
import io.sentry.core.hints.Retryable
-import io.sentry.core.protocol.SentryId
-import io.sentry.core.protocol.User
+import io.sentry.core.util.NoFlushTimeout
import java.io.File
-import java.io.FileNotFoundException
import java.nio.file.Files
-import java.nio.file.Paths
-import java.util.Date
-import java.util.UUID
+import java.nio.file.Path
+import kotlin.test.AfterTest
+import kotlin.test.BeforeTest
import kotlin.test.Test
-import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
-import kotlin.test.assertTrue
class EnvelopeSenderTest {
private class Fixture {
-
- var hub: IHub = mock()
- var envelopeReader: IEnvelopeReader = mock()
- var serializer: ISerializer = mock()
- var logger: ILogger = mock()
- var options: SentryOptions
+ var hub: IHub? = mock()
+ var logger: ILogger? = mock()
+ var serializer: ISerializer? = mock()
+ var options = SentryOptions().NoFlushTimeout()
init {
- options = SentryOptions()
options.isDebug = true
options.setLogger(logger)
}
fun getSut(): EnvelopeSender {
- return EnvelopeSender(hub, envelopeReader, serializer, logger, 15000)
+ return EnvelopeSender(hub!!, serializer!!, logger!!, options.flushTimeoutMillis)
}
}
+ private lateinit var tempDirectory: Path
private val fixture = Fixture()
- private fun getTempEnvelope(fileName: String): String {
- val testFile = this::class.java.classLoader.getResource(fileName)
- val testFileBytes = testFile!!.readBytes()
- val targetFile = File.createTempFile("temp-envelope", ".tmp")
- Files.write(Paths.get(targetFile.toURI()), testFileBytes)
- return targetFile.absolutePath
+ @BeforeTest
+ fun `before send`() {
+ tempDirectory = Files.createTempDirectory("send-cached-event-test")
}
- @Test
- fun `when envelopeReader returns null, file is deleted `() {
- whenever(fixture.envelopeReader.read(any())).thenReturn(null)
- val sut = fixture.getSut()
- val path = getTempEnvelope("envelope-event-attachment.txt")
- assertTrue(File(path).exists()) // sanity check
- sut.processEnvelopeFile(path, mock())
- assertFalse(File(path).exists())
- // Additionally make sure we have a error logged
- verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any())
+ @AfterTest
+ fun `after send`() {
+ File(tempDirectory.toUri()).delete()
}
@Test
- fun `when parser is EnvelopeReader and serializer returns SentryEvent, event captured, file is deleted `() {
- fixture.envelopeReader = EnvelopeReader()
- val expected = SentryEvent(SentryId(UUID.fromString("9ec79c33-ec99-42ab-8353-589fcb2e04dc")), Date())
- whenever(fixture.serializer.deserializeEvent(any())).thenReturn(expected)
+ fun `when directory doesn't exist, processDirectory logs and returns`() {
val sut = fixture.getSut()
- val path = getTempEnvelope("envelope-event-attachment.txt")
- assertTrue(File(path).exists()) // sanity check
- sut.processEnvelopeFile(path, mock())
-
- verify(fixture.hub).captureEvent(eq(expected), any())
- assertFalse(File(path).exists())
- // Additionally make sure we have no errors logged
- verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any())
- verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any())
+ sut.processDirectory(File("i don't exist"))
+ verify(fixture.logger)!!.log(eq(SentryLevel.WARNING), eq("Directory '%s' doesn't exist. No cached events to send."), any())
+ verifyNoMoreInteractions(fixture.hub)
}
@Test
- fun `when parser is EnvelopeReader and serializer returns SentryEnvelope, event captured, file is deleted `() {
- fixture.envelopeReader = EnvelopeReader()
- val session = Session("123", User(), "env", "release")
- val expected = SentryEnvelope(SentryId("3067d54967f84f20a2adfab5119156ce"), null, setOf())
- whenever(fixture.serializer.deserializeEnvelope(any())).thenReturn(expected)
- whenever(fixture.serializer.deserializeSession(any())).thenReturn(session)
+ fun `when directory is actually a file, processDirectory logs and returns`() {
val sut = fixture.getSut()
- val path = getTempEnvelope("envelope-session-start.txt")
- assertTrue(File(path).exists()) // sanity check
- sut.processEnvelopeFile(path, mock())
-
- verify(fixture.hub).captureEnvelope(any(), any())
- assertFalse(File(path).exists())
- // Additionally make sure we have no errors logged
- verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any())
- verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any())
+ val testFile = File(Files.createTempFile("send-cached-event-test", EnvelopeCache.SUFFIX_ENVELOPE_FILE).toUri())
+ testFile.deleteOnExit()
+ sut.processDirectory(testFile)
+ verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq("Cache dir %s is not a directory."), any())
+ verifyNoMoreInteractions(fixture.hub)
}
@Test
- fun `when parser is EnvelopeReader and serializer returns a null event, file error logged, no event captured `() {
- fixture.envelopeReader = EnvelopeReader()
- whenever(fixture.serializer.deserializeEvent(any())).thenReturn(null)
+ fun `when directory has non event files, processDirectory logs that`() {
val sut = fixture.getSut()
- val path = getTempEnvelope("envelope-event-attachment.txt")
- assertTrue(File(path).exists()) // sanity check
- sut.processEnvelopeFile(path, mock())
-
- // Additionally make sure we have no errors logged
- verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any())
- verify(fixture.hub, never()).captureEvent(any())
- assertFalse(File(path).exists())
+ val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", ".not-right-suffix").toUri())
+ sut.processDirectory(File(tempDirectory.toUri()))
+ testFile.deleteOnExit()
+ verify(fixture.logger)!!.log(eq(SentryLevel.DEBUG), eq("File '%s' doesn't match extension expected."), any())
+ verifyNoMoreInteractions(fixture.hub)
}
@Test
- fun `when parser is EnvelopeReader and serializer returns a null envelope, file error logged, no event captured `() {
- fixture.envelopeReader = EnvelopeReader()
- whenever(fixture.serializer.deserializeEnvelope(any())).thenReturn(null)
- whenever(fixture.serializer.deserializeSession(any())).thenReturn(null)
+ fun `when directory has event files, processDirectory captures with hub`() {
+ val event = SentryEvent()
+ val envelope = SentryEnvelope.fromEvent(fixture.serializer!!, event, null)
+ whenever(fixture.serializer!!.deserializeEnvelope(any())).thenReturn(envelope)
val sut = fixture.getSut()
- val path = getTempEnvelope("envelope-session-start.txt")
- assertTrue(File(path).exists()) // sanity check
- sut.processEnvelopeFile(path, mock())
-
- // Additionally make sure we have no errors logged
- verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any())
- verify(fixture.hub, never()).captureEvent(any())
- assertFalse(File(path).exists())
+ val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", EnvelopeCache.SUFFIX_ENVELOPE_FILE).toUri())
+ testFile.deleteOnExit()
+ sut.processDirectory(File(tempDirectory.toUri()))
+ verify(fixture.hub)!!.captureEnvelope(eq(envelope), any())
}
@Test
- fun `when processEnvelopeFile is called with a invalid path, logs error`() {
+ fun `when serializer throws, error is logged, file deleted`() {
+ val expected = RuntimeException()
+ whenever(fixture.serializer!!.deserializeEnvelope(any())).doThrow(expected)
val sut = fixture.getSut()
- sut.processEnvelopeFile(File.separator + "i-hope-it-doesnt-exist" + File.separator + "file.txt", mock())
- verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), argWhere { it is FileNotFoundException })
- }
-
- @Test
- fun `when hub is null, ctor throws`() {
- val clazz = Class.forName("io.sentry.core.EnvelopeSender")
- val ctor = clazz.getConstructor(IHub::class.java, IEnvelopeReader::class.java, ISerializer::class.java, ILogger::class.java, Long::class.java)
- val params = arrayOf(null, mock(), mock(), mock(), null)
- assertFailsWith { ctor.newInstance(params) }
- }
-
- @Test
- fun `when envelopeReader is null, ctor throws`() {
- val clazz = Class.forName("io.sentry.core.EnvelopeSender")
- val ctor = clazz.getConstructor(IHub::class.java, IEnvelopeReader::class.java, ISerializer::class.java, ILogger::class.java, Long::class.java)
- val params = arrayOf(mock(), null, mock(), mock(), 15000)
- assertFailsWith { ctor.newInstance(params) }
+ val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", EnvelopeCache.SUFFIX_ENVELOPE_FILE).toUri())
+ testFile.deleteOnExit()
+ sut.processFile(testFile, mock())
+ verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq(expected), eq("Failed to capture cached envelope %s"), eq(testFile.absolutePath))
+ verifyNoMoreInteractions(fixture.hub)
+ assertFalse(testFile.exists())
}
@Test
- fun `when serializer is null, ctor throws`() {
- val clazz = Class.forName("io.sentry.core.EnvelopeSender")
- val ctor = clazz.getConstructor(IHub::class.java, IEnvelopeReader::class.java, ISerializer::class.java, ILogger::class.java, Long::class.java)
- val params = arrayOf(mock(), mock(), null, mock(), 15000)
- assertFailsWith { ctor.newInstance(params) }
- }
-
- @Test
- fun `when logger is null, ctor throws`() {
- val clazz = Class.forName("io.sentry.core.EnvelopeSender")
- val ctor = clazz.getConstructor(IHub::class.java, IEnvelopeReader::class.java, ISerializer::class.java, ILogger::class.java, Long::class.java)
- val params = arrayOf(mock(), mock(), mock(), null, 15000)
- assertFailsWith { ctor.newInstance(params) }
- }
-
- @Test
- fun `when file name is null, should not be relevant`() {
- assertFalse(fixture.getSut().isRelevantFileName(null))
- }
-
- @Test
- fun `when file name is current prefix, should be ignored`() {
- assertFalse(fixture.getSut().isRelevantFileName(SessionCache.PREFIX_CURRENT_SESSION_FILE))
- }
-
- @Test
- fun `when file name is relevant, should return true`() {
- assertTrue(fixture.getSut().isRelevantFileName("123.envelope"))
+ fun `when hub throws, file gets deleted`() {
+ val expected = RuntimeException()
+ whenever(fixture.serializer!!.deserializeEnvelope(any())).doThrow(expected)
+ val sut = fixture.getSut()
+ val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", EnvelopeCache.SUFFIX_ENVELOPE_FILE).toUri())
+ testFile.deleteOnExit()
+ sut.processFile(testFile, any())
+ verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq(expected), eq("Failed to capture cached envelope %s"), eq(testFile.absolutePath))
+ verifyNoMoreInteractions(fixture.hub)
}
}
diff --git a/sentry-core/src/test/java/io/sentry/core/OutboxSenderTest.kt b/sentry-core/src/test/java/io/sentry/core/OutboxSenderTest.kt
new file mode 100644
index 000000000..56e91f914
--- /dev/null
+++ b/sentry-core/src/test/java/io/sentry/core/OutboxSenderTest.kt
@@ -0,0 +1,187 @@
+package io.sentry.core
+
+import com.nhaarman.mockitokotlin2.any
+import com.nhaarman.mockitokotlin2.argWhere
+import com.nhaarman.mockitokotlin2.eq
+import com.nhaarman.mockitokotlin2.mock
+import com.nhaarman.mockitokotlin2.never
+import com.nhaarman.mockitokotlin2.verify
+import com.nhaarman.mockitokotlin2.whenever
+import io.sentry.core.cache.EnvelopeCache
+import io.sentry.core.hints.Retryable
+import io.sentry.core.protocol.SentryId
+import java.io.File
+import java.io.FileNotFoundException
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.util.Date
+import java.util.UUID
+import kotlin.test.Test
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class OutboxSenderTest {
+ private class Fixture {
+
+ var hub: IHub = mock()
+ var envelopeReader: IEnvelopeReader = mock()
+ var serializer: ISerializer = mock()
+ var logger: ILogger = mock()
+ var options: SentryOptions
+
+ init {
+ options = SentryOptions()
+ options.isDebug = true
+ options.setLogger(logger)
+ }
+
+ fun getSut(): OutboxSender {
+ return OutboxSender(hub, envelopeReader, serializer, logger, 15000)
+ }
+ }
+
+ private val fixture = Fixture()
+
+ private fun getTempEnvelope(fileName: String): String {
+ val testFile = this::class.java.classLoader.getResource(fileName)
+ val testFileBytes = testFile!!.readBytes()
+ val targetFile = File.createTempFile("temp-envelope", ".tmp")
+ Files.write(Paths.get(targetFile.toURI()), testFileBytes)
+ return targetFile.absolutePath
+ }
+
+ @Test
+ fun `when envelopeReader returns null, file is deleted `() {
+ whenever(fixture.envelopeReader.read(any())).thenReturn(null)
+ val sut = fixture.getSut()
+ val path = getTempEnvelope("envelope-event-attachment.txt")
+ assertTrue(File(path).exists()) // sanity check
+ sut.processEnvelopeFile(path, mock())
+ assertFalse(File(path).exists())
+ // Additionally make sure we have a error logged
+ verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any())
+ }
+
+ @Test
+ fun `when parser is EnvelopeReader and serializer returns SentryEvent, event captured, file is deleted `() {
+ fixture.envelopeReader = EnvelopeReader()
+ val expected = SentryEvent(SentryId(UUID.fromString("9ec79c33-ec99-42ab-8353-589fcb2e04dc")), Date())
+ whenever(fixture.serializer.deserializeEvent(any())).thenReturn(expected)
+ val sut = fixture.getSut()
+ val path = getTempEnvelope("envelope-event-attachment.txt")
+ assertTrue(File(path).exists()) // sanity check
+ sut.processEnvelopeFile(path, mock())
+
+ verify(fixture.hub).captureEvent(eq(expected), any())
+ assertFalse(File(path).exists())
+ // Additionally make sure we have no errors logged
+ verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any())
+ verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any())
+ }
+
+ @Test
+ fun `when parser is EnvelopeReader and serializer returns SentryEnvelope, event captured, file is deleted `() {
+ fixture.envelopeReader = EnvelopeReader()
+
+ val event = SentryEvent(SentryId("9ec79c33ec9942ab8353589fcb2e04dc"), Date())
+ val expected = SentryEnvelope(SentryId("9ec79c33ec9942ab8353589fcb2e04dc"), null, setOf())
+ whenever(fixture.serializer.deserializeEnvelope(any())).thenReturn(expected)
+ whenever(fixture.serializer.deserializeEvent(any())).thenReturn(event)
+ val sut = fixture.getSut()
+ val path = getTempEnvelope("envelope-event-attachment.txt")
+ assertTrue(File(path).exists()) // sanity check
+ sut.processEnvelopeFile(path, mock())
+
+ verify(fixture.hub).captureEvent(any(), any())
+ assertFalse(File(path).exists())
+ // Additionally make sure we have no errors logged
+ verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any())
+ verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any())
+ }
+
+ @Test
+ fun `when parser is EnvelopeReader and serializer returns a null event, file error logged, no event captured `() {
+ fixture.envelopeReader = EnvelopeReader()
+ whenever(fixture.serializer.deserializeEvent(any())).thenReturn(null)
+ val sut = fixture.getSut()
+ val path = getTempEnvelope("envelope-event-attachment.txt")
+ assertTrue(File(path).exists()) // sanity check
+ sut.processEnvelopeFile(path, mock())
+
+ // Additionally make sure we have no errors logged
+ verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any())
+ verify(fixture.hub, never()).captureEvent(any())
+ assertFalse(File(path).exists())
+ }
+
+ @Test
+ fun `when parser is EnvelopeReader and serializer returns a null envelope, file error logged, no event captured `() {
+ fixture.envelopeReader = EnvelopeReader()
+ whenever(fixture.serializer.deserializeEnvelope(any())).thenReturn(null)
+ whenever(fixture.serializer.deserializeEvent(any())).thenReturn(null)
+ val sut = fixture.getSut()
+ val path = getTempEnvelope("envelope-event-attachment.txt")
+ assertTrue(File(path).exists()) // sanity check
+ sut.processEnvelopeFile(path, mock())
+
+ // Additionally make sure we have no errors logged
+ verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any())
+ verify(fixture.hub, never()).captureEvent(any())
+ assertFalse(File(path).exists())
+ }
+
+ @Test
+ fun `when processEnvelopeFile is called with a invalid path, logs error`() {
+ val sut = fixture.getSut()
+ sut.processEnvelopeFile(File.separator + "i-hope-it-doesnt-exist" + File.separator + "file.txt", mock())
+ verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), argWhere { it is FileNotFoundException })
+ }
+
+ @Test
+ fun `when hub is null, ctor throws`() {
+ val clazz = Class.forName("io.sentry.core.OutboxSender")
+ val ctor = clazz.getConstructor(IHub::class.java, IEnvelopeReader::class.java, ISerializer::class.java, ILogger::class.java, Long::class.java)
+ val params = arrayOf(null, mock(), mock(), mock(), null)
+ assertFailsWith { ctor.newInstance(params) }
+ }
+
+ @Test
+ fun `when envelopeReader is null, ctor throws`() {
+ val clazz = Class.forName("io.sentry.core.OutboxSender")
+ val ctor = clazz.getConstructor(IHub::class.java, IEnvelopeReader::class.java, ISerializer::class.java, ILogger::class.java, Long::class.java)
+ val params = arrayOf(mock(), null, mock(), mock(), 15000)
+ assertFailsWith { ctor.newInstance(params) }
+ }
+
+ @Test
+ fun `when serializer is null, ctor throws`() {
+ val clazz = Class.forName("io.sentry.core.OutboxSender")
+ val ctor = clazz.getConstructor(IHub::class.java, IEnvelopeReader::class.java, ISerializer::class.java, ILogger::class.java, Long::class.java)
+ val params = arrayOf(mock(), mock(), null, mock(), 15000)
+ assertFailsWith { ctor.newInstance(params) }
+ }
+
+ @Test
+ fun `when logger is null, ctor throws`() {
+ val clazz = Class.forName("io.sentry.core.OutboxSender")
+ val ctor = clazz.getConstructor(IHub::class.java, IEnvelopeReader::class.java, ISerializer::class.java, ILogger::class.java, Long::class.java)
+ val params = arrayOf(mock(), mock(), mock(), null, 15000)
+ assertFailsWith { ctor.newInstance(params) }
+ }
+
+ @Test
+ fun `when file name is null, should not be relevant`() {
+ assertFalse(fixture.getSut().isRelevantFileName(null))
+ }
+
+ @Test
+ fun `when file name is current prefix, should be ignored`() {
+ assertFalse(fixture.getSut().isRelevantFileName(EnvelopeCache.PREFIX_CURRENT_SESSION_FILE))
+ }
+
+ @Test
+ fun `when file name is relevant, should return true`() {
+ assertTrue(fixture.getSut().isRelevantFileName("123.envelope"))
+ }
+}
diff --git a/sentry-core/src/test/java/io/sentry/core/SendCachedEventFireAndForgetIntegrationTest.kt b/sentry-core/src/test/java/io/sentry/core/SendCachedEnvelopeFireAndForgetIntegrationTest.kt
similarity index 77%
rename from sentry-core/src/test/java/io/sentry/core/SendCachedEventFireAndForgetIntegrationTest.kt
rename to sentry-core/src/test/java/io/sentry/core/SendCachedEnvelopeFireAndForgetIntegrationTest.kt
index 9c242ff75..fb32a8e08 100644
--- a/sentry-core/src/test/java/io/sentry/core/SendCachedEventFireAndForgetIntegrationTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/SendCachedEnvelopeFireAndForgetIntegrationTest.kt
@@ -7,20 +7,20 @@ import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import kotlin.test.Test
import kotlin.test.assertFalse
-class SendCachedEventFireAndForgetIntegrationTest {
+class SendCachedEnvelopeFireAndForgetIntegrationTest {
private class Fixture {
var hub: IHub = mock()
var logger: ILogger = mock()
var options = SentryOptions()
- var callback = mock()
+ var callback = mock()
init {
options.isDebug = true
options.setLogger(logger)
}
- fun getSut(): SendCachedEventFireAndForgetIntegration {
- return SendCachedEventFireAndForgetIntegration(callback)
+ fun getSut(): SendCachedEnvelopeFireAndForgetIntegration {
+ return SendCachedEnvelopeFireAndForgetIntegration(callback)
}
}
@@ -55,15 +55,15 @@ class SendCachedEventFireAndForgetIntegrationTest {
@Test
fun `when Factory returns null, register logs and exit`() {
- val sut = SendCachedEventFireAndForgetIntegration(CustomFactory())
+ val sut = SendCachedEnvelopeFireAndForgetIntegration(CustomFactory())
fixture.options.cacheDirPath = "abc"
sut.register(fixture.hub, fixture.options)
verify(fixture.logger).log(eq(SentryLevel.ERROR), eq("SendFireAndForget factory is null."))
verifyNoMoreInteractions(fixture.hub)
}
- private class CustomFactory : SendCachedEventFireAndForgetIntegration.SendFireAndForgetFactory {
- override fun create(hub: IHub?, options: SentryOptions?): SendCachedEventFireAndForgetIntegration.SendFireAndForget? {
+ private class CustomFactory : SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory {
+ override fun create(hub: IHub?, options: SentryOptions?): SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget? {
return null
}
}
diff --git a/sentry-core/src/test/java/io/sentry/core/SendCachedEventTest.kt b/sentry-core/src/test/java/io/sentry/core/SendCachedEventTest.kt
deleted file mode 100644
index 0d8453dd4..000000000
--- a/sentry-core/src/test/java/io/sentry/core/SendCachedEventTest.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-package io.sentry.core
-
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.doThrow
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
-import com.nhaarman.mockitokotlin2.whenever
-import io.sentry.core.cache.DiskCache
-import io.sentry.core.hints.Retryable
-import io.sentry.core.util.NoFlushTimeout
-import java.io.File
-import java.nio.file.Files
-import java.nio.file.Path
-import kotlin.test.AfterTest
-import kotlin.test.BeforeTest
-import kotlin.test.Test
-import kotlin.test.assertFalse
-
-class SendCachedEventTest {
- private class Fixture {
- var hub: IHub? = mock()
- var logger: ILogger? = mock()
- var serializer: ISerializer? = mock()
- var options = SentryOptions().NoFlushTimeout()
-
- init {
- options.isDebug = true
- options.setLogger(logger)
- }
-
- fun getSut(): SendCachedEvent {
- return SendCachedEvent(serializer!!, hub!!, logger!!, options.flushTimeoutMillis)
- }
- }
-
- private lateinit var tempDirectory: Path
- private val fixture = Fixture()
-
- @BeforeTest
- fun `before send`() {
- tempDirectory = Files.createTempDirectory("send-cached-event-test")
- }
-
- @AfterTest
- fun `after send`() {
- File(tempDirectory.toUri()).delete()
- }
-
- @Test
- fun `when directory doesn't exist, processDirectory logs and returns`() {
- val sut = fixture.getSut()
- sut.processDirectory(File("i don't exist"))
- verify(fixture.logger)!!.log(eq(SentryLevel.WARNING), eq("Directory '%s' doesn't exist. No cached events to send."), any())
- verifyNoMoreInteractions(fixture.hub)
- }
-
- @Test
- fun `when directory is actually a file, processDirectory logs and returns`() {
- val sut = fixture.getSut()
- val testFile = File(Files.createTempFile("send-cached-event-test", DiskCache.FILE_SUFFIX).toUri())
- testFile.deleteOnExit()
- sut.processDirectory(testFile)
- verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq("Cache dir %s is not a directory."), any())
- verifyNoMoreInteractions(fixture.hub)
- }
-
- @Test
- fun `when directory has non event files, processDirectory logs that`() {
- val sut = fixture.getSut()
- val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", ".not-right-suffix").toUri())
- sut.processDirectory(File(tempDirectory.toUri()))
- testFile.deleteOnExit()
- verify(fixture.logger)!!.log(eq(SentryLevel.DEBUG), eq("File '%s' doesn't match extension expected."), any())
- verifyNoMoreInteractions(fixture.hub)
- }
-
- @Test
- fun `when directory has event files, processDirectory captures with hub`() {
- val expected = SentryEvent()
- whenever(fixture.serializer!!.deserializeEvent(any())).thenReturn(expected)
- val sut = fixture.getSut()
- val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", DiskCache.FILE_SUFFIX).toUri())
- testFile.deleteOnExit()
- sut.processDirectory(File(tempDirectory.toUri()))
- verify(fixture.hub)!!.captureEvent(eq(expected), any())
- }
-
- @Test
- fun `when serializer throws, error is logged, file deleted`() {
- val expected = RuntimeException()
- whenever(fixture.serializer!!.deserializeEvent(any())).doThrow(expected)
- val sut = fixture.getSut()
- val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", DiskCache.FILE_SUFFIX).toUri())
- testFile.deleteOnExit()
- sut.processFile(testFile, mock())
- verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq(expected), eq("Failed to capture cached event %s"), eq(testFile.absolutePath))
- verifyNoMoreInteractions(fixture.hub)
- assertFalse(testFile.exists())
- }
-
- @Test
- fun `when hub throws, file gets deleted`() {
- whenever(fixture.serializer!!.deserializeEvent(any())).thenReturn(SentryEvent())
- val expected = RuntimeException()
- whenever(fixture.serializer!!.deserializeEvent(any())).doThrow(expected)
- val sut = fixture.getSut()
- val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", DiskCache.FILE_SUFFIX).toUri())
- testFile.deleteOnExit()
- sut.processFile(testFile, any())
- verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq(expected), eq("Failed to capture cached event %s"), eq(testFile.absolutePath))
- verifyNoMoreInteractions(fixture.hub)
- }
-}
diff --git a/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt b/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt
index 471e22246..227bfb1a8 100644
--- a/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt
@@ -2,10 +2,8 @@ package io.sentry.core
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.anyOrNull
-import com.nhaarman.mockitokotlin2.argWhere
import com.nhaarman.mockitokotlin2.check
import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.isNull
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.mockingDetails
import com.nhaarman.mockitokotlin2.never
@@ -15,8 +13,6 @@ import com.nhaarman.mockitokotlin2.whenever
import io.sentry.core.hints.ApplyScopeData
import io.sentry.core.hints.Cached
import io.sentry.core.hints.DiskFlushNotification
-import io.sentry.core.hints.SessionEndHint
-import io.sentry.core.hints.SessionUpdateHint
import io.sentry.core.protocol.Mechanism
import io.sentry.core.protocol.Request
import io.sentry.core.protocol.SdkVersion
@@ -26,7 +22,9 @@ import io.sentry.core.protocol.User
import io.sentry.core.transport.AsyncConnection
import io.sentry.core.transport.HttpTransport
import io.sentry.core.transport.ITransportGate
+import java.io.ByteArrayInputStream
import java.io.IOException
+import java.io.InputStreamReader
import java.net.URL
import java.util.UUID
import kotlin.test.Ignore
@@ -48,8 +46,10 @@ class SentryClientTest {
name = "test"
version = "1.2.3"
}
+ setSerializer(GsonSerializer(mock(), envelopeReader))
}
var connection: AsyncConnection = mock()
+
fun getSut() = SentryClient(sentryOptions, connection)
}
@@ -120,17 +120,22 @@ class SentryClientTest {
val sut = fixture.getSut()
val event = SentryEvent()
sut.captureEvent(event)
- verify(fixture.connection, never()).send(event)
+ verify(fixture.connection, never()).send(any())
}
@Test
fun `when beforeSend is returns new instance, new instance is sent`() {
- val expected = SentryEvent()
+ val expected = SentryEvent().apply {
+ setTag("test", "test")
+ }
fixture.sentryOptions.setBeforeSend { _, _ -> expected }
val sut = fixture.getSut()
val actual = SentryEvent()
sut.captureEvent(actual)
- verify(fixture.connection).send(eq(expected), isNull())
+ verify(fixture.connection).send(check {
+ val event = getEventFromData(it.items.first().data)
+ assertEquals("test", event.tags["test"])
+ }, anyOrNull())
verifyNoMoreInteractions(fixture.connection)
}
@@ -157,7 +162,7 @@ class SentryClientTest {
val sut = fixture.getSut()
val expectedHint = Object()
sut.captureEvent(event, expectedHint)
- verify(fixture.connection).send(event, expectedHint)
+ verify(fixture.connection).send(any(), eq(expectedHint))
}
@Test
@@ -422,6 +427,7 @@ class SentryClientTest {
val event = SentryEvent()
val scope = createScope()
val processor = mock()
+ whenever(processor.process(any(), anyOrNull())).thenReturn(event)
scope.addEventProcessor(processor)
val sut = fixture.getSut()
@@ -577,39 +583,6 @@ class SentryClientTest {
}
}
- @Test
- fun `When event comes from uncaughtException, captureSession should use SessionEndHint`() {
- fixture.sentryOptions.release = "a@1+1"
- val sut = fixture.getSut()
-
- val event = SentryEvent().apply {
- exceptions = createNonHandledException()
- }
- val scope = Scope(fixture.sentryOptions)
- scope.startSession()
- val hint = mock()
- sut.captureEvent(event, scope, hint)
- verify(fixture.connection).send(any(), argWhere {
- it is SessionEndHint
- })
- }
-
- @Test
- fun `When event is not from uncaughtException, captureSession should use SessionUpdateHint`() {
- fixture.sentryOptions.release = "a@1+1"
- val sut = fixture.getSut()
-
- val event = SentryEvent().apply {
- exceptions = createNonHandledException()
- }
- val scope = Scope(fixture.sentryOptions)
- scope.startSession()
- sut.captureEvent(event, scope)
- verify(fixture.connection).send(any(), argWhere {
- it is SessionUpdateHint
- })
- }
-
@Test
fun `when captureEvent with sampling, session is still updated`() {
fixture.sentryOptions.sampleRate = 1.0
@@ -631,13 +604,13 @@ class SentryClientTest {
fun `when context property is missing on the event, property from scope contexts is applied`() {
val sut = fixture.getSut()
- val event = SentryEvent()
val scope = Scope(fixture.sentryOptions)
scope.setContexts("key", "value")
scope.startSession().current
- sut.captureEvent(event, scope, null)
- verify(fixture.connection).send(check() {
- assertEquals("value", it.contexts["key"])
+ sut.captureEvent(SentryEvent(), scope, null)
+ verify(fixture.connection).send(check {
+ val event = getEventFromData(it.items.first().data)
+ assertEquals("value", event.contexts["key"])
}, anyOrNull())
}
@@ -651,8 +624,9 @@ class SentryClientTest {
scope.setContexts("key", "scope value")
scope.startSession().current
sut.captureEvent(event, scope, null)
- verify(fixture.connection).send(check() {
- assertEquals("event value", it.contexts["key"])
+ verify(fixture.connection).send(check {
+ val eventFromData = getEventFromData(it.items.first().data)
+ assertEquals("event value", eventFromData.contexts["key"])
}, anyOrNull())
}
@@ -705,6 +679,11 @@ class SentryClientTest {
return listOf(exception)
}
+ private fun getEventFromData(data: ByteArray): SentryEvent {
+ val inputStream = InputStreamReader(ByteArrayInputStream(data))
+ return fixture.sentryOptions.serializer.deserializeEvent(inputStream)
+ }
+
internal class CustomCachedApplyScopeDataHint : Cached, ApplyScopeData
internal class DiskFlushNotificationHint : DiskFlushNotification {
diff --git a/sentry-core/src/test/java/io/sentry/core/SentryOptionsTest.kt b/sentry-core/src/test/java/io/sentry/core/SentryOptionsTest.kt
index 5068179e0..89732b673 100644
--- a/sentry-core/src/test/java/io/sentry/core/SentryOptionsTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/SentryOptionsTest.kt
@@ -106,19 +106,6 @@ class SentryOptionsTest {
assertEquals("${File.separator}test${File.separator}outbox", options.outboxPath)
}
- @Test
- fun `when there's no cacheDirPath, sessionPath returns null`() {
- val options = SentryOptions()
- assertNull(options.sessionsPath)
- }
-
- @Test
- fun `when cacheDirPath is set, sessionPath concatenate sessions path`() {
- val options = SentryOptions()
- options.cacheDirPath = "${File.separator}test"
- assertEquals("${File.separator}test${File.separator}sessions", options.sessionsPath)
- }
-
@Test
fun `SentryOptions creates SentryExecutorService on ctor`() {
val options = SentryOptions()
diff --git a/sentry-core/src/test/java/io/sentry/core/SentryTest.kt b/sentry-core/src/test/java/io/sentry/core/SentryTest.kt
index 348946fa9..61b579c6f 100644
--- a/sentry-core/src/test/java/io/sentry/core/SentryTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/SentryTest.kt
@@ -46,20 +46,6 @@ class SentryTest {
file.deleteOnExit()
}
- @Test
- fun `sessionDir should be created at initialization`() {
- var sentryOptions: SentryOptions? = null
- Sentry.init {
- it.dsn = "http://key@localhost/proj"
- it.cacheDirPath = getTempPath()
- sentryOptions = it
- }
-
- val file = File(sentryOptions!!.sessionsPath!!)
- assertTrue(file.exists())
- file.deleteOnExit()
- }
-
@Test
fun `Init sets SystemOutLogger if logger is NoOp and debug is enabled`() {
var sentryOptions: SentryOptions? = null
diff --git a/sentry-core/src/test/java/io/sentry/core/cache/DiskCacheTest.kt b/sentry-core/src/test/java/io/sentry/core/cache/DiskCacheTest.kt
deleted file mode 100644
index d79e47b2a..000000000
--- a/sentry-core/src/test/java/io/sentry/core/cache/DiskCacheTest.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-package io.sentry.core.cache
-
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.doAnswer
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.whenever
-import io.sentry.core.ISerializer
-import io.sentry.core.SentryEvent
-import io.sentry.core.SentryOptions
-import io.sentry.core.protocol.SentryId
-import java.io.Reader
-import java.io.Writer
-import java.nio.file.Files
-import java.nio.file.Path
-import kotlin.test.AfterTest
-import kotlin.test.Test
-import kotlin.test.assertEquals
-
-class DiskCacheTest {
- private class Fixture {
- val maxSize = 5
- val dir: Path = Files.createTempDirectory("sentry-disk-cache-test")
-
- fun getSUT(): DiskCache {
- val options = SentryOptions()
- options.cacheDirSize = maxSize
- options.cacheDirPath = dir.toAbsolutePath().toFile().absolutePath
-
- val serializer = mock()
- doAnswer {
- val event = it.arguments[0] as SentryEvent
- val writer = it.arguments[1] as Writer
-
- writer.write(event.eventId.toString())
- }.whenever(serializer).serialize(any(), any())
-
- whenever(serializer.deserializeEvent(any())).thenAnswer {
- val reader = it.arguments[0] as Reader
-
- val ret = SentryEvent()
- val text = reader.readText()
- ret.eventId = SentryId(text)
- ret
- }
-
- options.setSerializer(serializer)
-
- return DiskCache(options)
- }
- }
-
- private val fixture = Fixture()
-
- @AfterTest
- fun cleanUp() {
- fixture.dir.toFile().listFiles()?.forEach { it.delete() }
- Files.delete(fixture.dir)
- }
-
- @Test
- fun `stores events`() {
- val cache = fixture.getSUT()
-
- val nofFiles = { fixture.dir.toFile().list()?.size }
-
- assertEquals(0, nofFiles())
-
- cache.store(SentryEvent())
-
- assertEquals(1, nofFiles())
- }
-
- @Test
- fun `limits the number of stored events`() {
- val cache = fixture.getSUT()
-
- val nofFiles = { fixture.dir.toFile().list()?.size }
-
- assertEquals(0, nofFiles())
-
- (1..fixture.maxSize + 1).forEach { _ ->
- cache.store(SentryEvent(Exception()))
- }
-
- assertEquals(fixture.maxSize, nofFiles())
- }
-
- @Test
- fun `tolerates discarding unknown event`() {
- val cache = fixture.getSUT()
-
- cache.discard(SentryEvent())
-
- // no exception thrown
- }
-
- @Test
- fun `reads the event back`() {
-
- val cache = fixture.getSUT()
-
- val event = SentryEvent()
-
- cache.store(event)
-
- val readEvents = cache.toList()
-
- assertEquals(1, readEvents.size)
-
- val readEvent = readEvents[0]
-
- assertEquals(event.eventId.toString(), readEvent.eventId.toString())
- }
-}
diff --git a/sentry-core/src/test/java/io/sentry/core/cache/SessionCacheTest.kt b/sentry-core/src/test/java/io/sentry/core/cache/EnvelopeCacheTest.kt
similarity index 69%
rename from sentry-core/src/test/java/io/sentry/core/cache/SessionCacheTest.kt
rename to sentry-core/src/test/java/io/sentry/core/cache/EnvelopeCacheTest.kt
index b4d2e1586..fdfc1cf86 100644
--- a/sentry-core/src/test/java/io/sentry/core/cache/SessionCacheTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/cache/EnvelopeCacheTest.kt
@@ -11,12 +11,10 @@ import io.sentry.core.SentryEnvelope
import io.sentry.core.SentryLevel
import io.sentry.core.SentryOptions
import io.sentry.core.Session
-import io.sentry.core.cache.SessionCache.PREFIX_CURRENT_SESSION_FILE
-import io.sentry.core.cache.SessionCache.SUFFIX_CURRENT_SESSION_FILE
-import io.sentry.core.cache.SessionCache.SUFFIX_ENVELOPE_FILE
+import io.sentry.core.cache.EnvelopeCache.PREFIX_CURRENT_SESSION_FILE
+import io.sentry.core.cache.EnvelopeCache.SUFFIX_CURRENT_SESSION_FILE
import io.sentry.core.hints.SessionEndHint
import io.sentry.core.hints.SessionStartHint
-import io.sentry.core.hints.SessionUpdateHint
import io.sentry.core.protocol.User
import java.io.File
import java.nio.file.Files
@@ -27,7 +25,7 @@ import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
-class SessionCacheTest {
+class EnvelopeCacheTest {
private class Fixture {
val maxSize = 5
val dir: Path = Files.createTempDirectory("sentry-session-cache-test")
@@ -36,9 +34,7 @@ class SessionCacheTest {
val logger = mock()
fun getSUT(): IEnvelopeCache {
- options.sessionsDirSize = maxSize
options.cacheDirPath = dir.toAbsolutePath().toFile().absolutePath
- File(options.sessionsPath!!).mkdirs()
whenever(serializer.deserializeSession(any())).thenAnswer {
Session("dis", User(), "env", "rel")
@@ -48,7 +44,7 @@ class SessionCacheTest {
options.setSerializer(serializer)
options.isDebug = true
- return SessionCache(options)
+ return EnvelopeCache(options)
}
}
@@ -58,7 +54,7 @@ class SessionCacheTest {
fun `stores envelopes`() {
val cache = fixture.getSUT()
- val file = File(fixture.options.sessionsPath!!)
+ val file = File(fixture.options.cacheDirPath!!)
val nofFiles = { file.list()?.size }
assertEquals(0, nofFiles())
@@ -70,24 +66,6 @@ class SessionCacheTest {
file.deleteRecursively()
}
- @Test
- fun `limits the number of stored envelopes`() {
- val cache = fixture.getSUT()
-
- val file = File(fixture.options.sessionsPath!!)
- val nofFiles = { file.list()?.size }
-
- assertEquals(0, nofFiles())
-
- (1..fixture.maxSize + 1).forEach { _ ->
- cache.store(SentryEnvelope.fromSession(fixture.serializer, createSession(), null))
- }
-
- assertEquals(fixture.maxSize, nofFiles())
-
- file.deleteRecursively()
- }
-
@Test
fun `tolerates discarding unknown envelope`() {
val cache = fixture.getSUT()
@@ -101,12 +79,12 @@ class SessionCacheTest {
fun `creates current file on session start`() {
val cache = fixture.getSUT()
- val file = File(fixture.options.sessionsPath!!)
+ val file = File(fixture.options.cacheDirPath!!)
val envelope = SentryEnvelope.fromSession(fixture.serializer, createSession(), null)
cache.store(envelope, SessionStartHint())
- val currentFile = File(fixture.options.sessionsPath!!, "$PREFIX_CURRENT_SESSION_FILE$SUFFIX_CURRENT_SESSION_FILE")
+ val currentFile = File(fixture.options.cacheDirPath!!, "$PREFIX_CURRENT_SESSION_FILE$SUFFIX_CURRENT_SESSION_FILE")
assertTrue(currentFile.exists())
file.deleteRecursively()
@@ -116,12 +94,12 @@ class SessionCacheTest {
fun `deletes current file on session end`() {
val cache = fixture.getSUT()
- val file = File(fixture.options.sessionsPath!!)
+ val file = File(fixture.options.cacheDirPath!!)
val envelope = SentryEnvelope.fromSession(fixture.serializer, createSession(), null)
cache.store(envelope, SessionStartHint())
- val currentFile = File(fixture.options.sessionsPath!!, "$PREFIX_CURRENT_SESSION_FILE$SUFFIX_CURRENT_SESSION_FILE")
+ val currentFile = File(fixture.options.cacheDirPath!!, "$PREFIX_CURRENT_SESSION_FILE$SUFFIX_CURRENT_SESSION_FILE")
assertTrue(currentFile.exists())
cache.store(envelope, SessionEndHint())
@@ -130,39 +108,16 @@ class SessionCacheTest {
file.deleteRecursively()
}
- @Test
- fun `updates current file on session update, but do not create a new envelope`() {
- val cache = fixture.getSUT()
-
- val file = File(fixture.options.sessionsPath!!)
-
- val envelope = SentryEnvelope.fromSession(fixture.serializer, createSession(), null)
- cache.store(envelope, SessionStartHint())
-
- val currentFile = File(fixture.options.sessionsPath!!, "$PREFIX_CURRENT_SESSION_FILE$SUFFIX_CURRENT_SESSION_FILE")
- assertTrue(currentFile.exists())
-
- val newEnvelope = SentryEnvelope.fromSession(fixture.serializer, createSession(), null)
-
- cache.store(newEnvelope, SessionUpdateHint())
- assertTrue(currentFile.exists())
-
- val newFile = File(file.absolutePath, "${newEnvelope.header.eventId}$SUFFIX_ENVELOPE_FILE")
- assertFalse(newFile.exists())
-
- file.deleteRecursively()
- }
-
@Test
fun `updates current file on session update and read it back`() {
val cache = fixture.getSUT()
- val file = File(fixture.options.sessionsPath!!)
+ val file = File(fixture.options.cacheDirPath!!)
val envelope = SentryEnvelope.fromSession(fixture.serializer, createSession(), null)
cache.store(envelope, SessionStartHint())
- val currentFile = File(fixture.options.sessionsPath!!, "$PREFIX_CURRENT_SESSION_FILE$SUFFIX_CURRENT_SESSION_FILE")
+ val currentFile = File(fixture.options.cacheDirPath!!, "$PREFIX_CURRENT_SESSION_FILE$SUFFIX_CURRENT_SESSION_FILE")
assertTrue(currentFile.exists())
val session = fixture.serializer.deserializeSession(currentFile.bufferedReader(Charsets.UTF_8))
@@ -190,8 +145,8 @@ class SessionCacheTest {
fun `when session start, current file already exist and crash marker file exist, end session and delete marker file`() {
val cache = fixture.getSUT()
- val file = File(fixture.options.sessionsPath!!)
- val markerFile = File(fixture.options.cacheDirPath!!, SessionCache.CRASH_MARKER_FILE)
+ val file = File(fixture.options.cacheDirPath!!)
+ val markerFile = File(fixture.options.cacheDirPath!!, EnvelopeCache.CRASH_MARKER_FILE)
markerFile.mkdirs()
assertTrue(markerFile.exists())
@@ -209,8 +164,8 @@ class SessionCacheTest {
@Test
fun `when session start, current file already exist and crash marker file exist, end session with given timestamp`() {
val cache = fixture.getSUT()
- val file = File(fixture.options.sessionsPath!!)
- val markerFile = File(fixture.options.cacheDirPath!!, SessionCache.CRASH_MARKER_FILE)
+ val file = File(fixture.options.cacheDirPath!!)
+ val markerFile = File(fixture.options.cacheDirPath!!, EnvelopeCache.CRASH_MARKER_FILE)
File(fixture.options.cacheDirPath!!, ".sentry-native").mkdirs()
markerFile.createNewFile()
val date = "2020-02-07T14:16:00.000Z"
diff --git a/sentry-core/src/test/java/io/sentry/core/transport/AsyncConnectionTest.kt b/sentry-core/src/test/java/io/sentry/core/transport/AsyncConnectionTest.kt
index 2b7b8dc05..0e54785eb 100644
--- a/sentry-core/src/test/java/io/sentry/core/transport/AsyncConnectionTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/transport/AsyncConnectionTest.kt
@@ -17,7 +17,6 @@ import io.sentry.core.SentryEvent
import io.sentry.core.SentryOptions
import io.sentry.core.Session
import io.sentry.core.cache.IEnvelopeCache
-import io.sentry.core.cache.IEventCache
import io.sentry.core.dsnString
import io.sentry.core.protocol.User
import java.io.IOException
@@ -30,8 +29,7 @@ class AsyncConnectionTest {
private class Fixture {
var transport = mock()
var transportGate = mock()
- var eventCache = mock()
- var sessionCache = mock()
+ var envelopeCache = mock()
var executor = mock()
var sentryOptions: SentryOptions = SentryOptions().apply {
dsn = dsnString
@@ -46,70 +44,34 @@ class AsyncConnectionTest {
}
fun getSUT(): AsyncConnection {
- return AsyncConnection(transport, transportGate, eventCache, sessionCache, executor, sentryOptions)
+ return AsyncConnection(transport, transportGate, envelopeCache, executor, sentryOptions)
}
}
private val fixture = Fixture()
@Test
- fun `successful send discards the event from cache`() {
- // given
- val ev = mock()
- whenever(fixture.transportGate.isConnected).thenReturn(true)
- whenever(fixture.transport.send(any())).thenReturn(TransportResult.success())
-
- // when
- fixture.getSUT().send(ev)
-
- // then
- val order = inOrder(fixture.transport, fixture.eventCache)
-
- // because storeBeforeSend is enabled by default
- order.verify(fixture.eventCache).store(eq(ev))
-
- order.verify(fixture.transport).send(check {
- assertEquals(ev.eventId, it.header.eventId)
- })
- order.verify(fixture.eventCache).discard(eq(ev))
- }
-
- @Test
- fun `successful send discards the session from cache`() {
+ fun `successful send discards the envelope from cache`() {
// given
val envelope = SentryEnvelope.fromSession(fixture.sentryOptions.serializer, createSession(), null)
whenever(fixture.transportGate.isConnected).thenReturn(true)
- whenever(fixture.transport.send(any())).thenReturn(TransportResult.success())
+ whenever(fixture.transport.send(any())).thenReturn(TransportResult.success())
// when
fixture.getSUT().send(envelope)
// then
- val order = inOrder(fixture.transport, fixture.sessionCache)
+ val order = inOrder(fixture.transport, fixture.envelopeCache)
// because storeBeforeSend is enabled by default
- order.verify(fixture.sessionCache).store(eq(envelope), anyOrNull())
+ order.verify(fixture.envelopeCache).store(eq(envelope), anyOrNull())
order.verify(fixture.transport).send(eq(envelope))
- order.verify(fixture.sessionCache).discard(eq(envelope))
+ order.verify(fixture.envelopeCache).discard(eq(envelope))
}
@Test
- fun `stores event in cache if sending is not allowed`() {
- // given
- val ev = mock()
- whenever(fixture.transportGate.isConnected).thenReturn(false)
-
- // when
- fixture.getSUT().send(ev)
-
- // then
- verify(fixture.eventCache).store(eq(ev))
- verify(fixture.transport).isRetryAfter(any())
- }
-
- @Test
- fun `stores session in cache if sending is not allowed`() {
+ fun `stores envelope in cache if sending is not allowed`() {
// given
val envelope = SentryEnvelope.fromSession(fixture.sentryOptions.serializer, createSession(), null)
whenever(fixture.transportGate.isConnected).thenReturn(false)
@@ -118,42 +80,16 @@ class AsyncConnectionTest {
fixture.getSUT().send(envelope)
// then
- verify(fixture.sessionCache).store(eq(envelope), anyOrNull())
+ verify(fixture.envelopeCache).store(eq(envelope), anyOrNull())
verify(fixture.transport).isRetryAfter(any())
}
@Test
- fun `stores event after unsuccessful send`() {
- // given
- val ev = mock()
- whenever(fixture.transportGate.isConnected).thenReturn(true)
- whenever(fixture.transport.send(any())).thenReturn(TransportResult.error(500))
-
- // when
- try {
- fixture.getSUT().send(ev)
- } catch (e: IllegalStateException) {
- // expected - this is how the AsyncConnection signals failure to the executor for it to retry
- }
-
- // then
- val order = inOrder(fixture.transport, fixture.eventCache)
-
- // because storeBeforeSend is enabled by default
- order.verify(fixture.eventCache).store(eq(ev))
-
- order.verify(fixture.transport).send(check {
- assertEquals(ev.eventId, it.header.eventId)
- })
- verify(fixture.eventCache, never()).discard(any())
- }
-
- @Test
- fun `stores session after unsuccessful send`() {
+ fun `stores envelope after unsuccessful send`() {
// given
val envelope = SentryEnvelope.fromSession(fixture.sentryOptions.serializer, createSession(), null)
whenever(fixture.transportGate.isConnected).thenReturn(true)
- whenever(fixture.transport.send(any())).thenReturn(TransportResult.error(500))
+ whenever(fixture.transport.send(any())).thenReturn(TransportResult.error(500))
// when
try {
@@ -163,43 +99,21 @@ class AsyncConnectionTest {
}
// then
- val order = inOrder(fixture.transport, fixture.sessionCache)
+ val order = inOrder(fixture.transport, fixture.envelopeCache)
// because storeBeforeSend is enabled by default
- order.verify(fixture.sessionCache).store(eq(envelope), anyOrNull())
+ order.verify(fixture.envelopeCache).store(eq(envelope), anyOrNull())
order.verify(fixture.transport).send(eq(envelope))
- verify(fixture.eventCache, never()).discard(any())
- }
-
- @Test
- fun `stores event after send failure`() {
- // given
- val ev = mock()
- whenever(fixture.transportGate.isConnected).thenReturn(true)
- whenever(fixture.transport.send(any())).thenThrow(IOException())
-
- // when
- try {
- fixture.getSUT().send(ev)
- } catch (e: IllegalStateException) {
- // expected - this is how the AsyncConnection signals failure to the executor for it to retry
- }
-
- // then
- val order = inOrder(fixture.transport, fixture.eventCache)
- order.verify(fixture.transport).send(check {
- assertEquals(ev.eventId, it.header.eventId)
- })
- verify(fixture.eventCache, never()).discard(any())
+ verify(fixture.envelopeCache, never()).discard(any())
}
@Test
- fun `stores session after send failure`() {
+ fun `stores envelope after send failure`() {
// given
val envelope = SentryEnvelope.fromSession(fixture.sentryOptions.serializer, createSession(), null)
whenever(fixture.transportGate.isConnected).thenReturn(true)
- whenever(fixture.transport.send(any())).thenThrow(IOException())
+ whenever(fixture.transport.send(any())).thenThrow(IOException())
// when
try {
@@ -209,9 +123,9 @@ class AsyncConnectionTest {
}
// then
- val order = inOrder(fixture.transport, fixture.sessionCache)
+ val order = inOrder(fixture.transport, fixture.envelopeCache)
order.verify(fixture.transport).send(eq(envelope))
- verify(fixture.sessionCache, never()).discard(any())
+ verify(fixture.envelopeCache, never()).discard(any())
}
@Test
@@ -219,9 +133,10 @@ class AsyncConnectionTest {
// given
val ev = mock()
whenever(fixture.transport.isRetryAfter(any())).thenReturn(true)
+ val envelope = SentryEnvelope.fromEvent(fixture.sentryOptions.serializer, ev, null)
// when
- fixture.getSUT().send(ev)
+ fixture.getSUT().send(envelope)
// then
verify(fixture.executor, never()).submit(any())
@@ -232,9 +147,10 @@ class AsyncConnectionTest {
// given
val ev = mock()
whenever(fixture.transport.isRetryAfter(any())).thenReturn(false)
+ val envelope = SentryEnvelope.fromEvent(fixture.sentryOptions.serializer, ev, null)
// when
- fixture.getSUT().send(ev)
+ fixture.getSUT().send(envelope)
// then
verify(fixture.executor).submit(any())
@@ -263,7 +179,7 @@ class AsyncConnectionTest {
fixture.getSUT().send(envelope, CachedEvent())
// then
- verify(fixture.sessionCache).discard(any())
+ verify(fixture.envelopeCache).discard(any())
}
@Test
@@ -276,7 +192,7 @@ class AsyncConnectionTest {
fixture.getSUT().send(envelope)
// then
- verify(fixture.sessionCache, never()).discard(any())
+ verify(fixture.envelopeCache, never()).discard(any())
}
@Test
@@ -313,12 +229,13 @@ class AsyncConnectionTest {
// given
val ev = mock()
whenever(fixture.transport.isRetryAfter(any())).thenReturn(true)
+ val envelope = SentryEnvelope.fromEvent(fixture.sentryOptions.serializer, ev, null)
// when
- fixture.getSUT().send(ev, CachedEvent())
+ fixture.getSUT().send(envelope, CachedEvent())
// then
- verify(fixture.eventCache).discard(any())
+ verify(fixture.envelopeCache).discard(any())
}
@Test
@@ -326,12 +243,13 @@ class AsyncConnectionTest {
// given
val ev = mock()
whenever(fixture.transport.isRetryAfter(any())).thenReturn(true)
+ val envelope = SentryEnvelope.fromEvent(fixture.sentryOptions.serializer, ev, null)
// when
- fixture.getSUT().send(ev)
+ fixture.getSUT().send(envelope)
// then
- verify(fixture.eventCache, never()).discard(any())
+ verify(fixture.envelopeCache, never()).discard(any())
}
private fun createSession(): Session {
From 3915ca4154642f38bc9850ab951053f9364e5388 Mon Sep 17 00:00:00 2001
From: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com>
Date: Thu, 3 Sep 2020 13:41:00 +0200
Subject: [PATCH 17/28] fix: enable session tracking by default on sample
(#538)
---
sentry-samples/sentry-samples-android/build.gradle.kts | 6 ++++--
.../sentry-samples-android/src/main/AndroidManifest.xml | 6 +++---
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/sentry-samples/sentry-samples-android/build.gradle.kts b/sentry-samples/sentry-samples-android/build.gradle.kts
index 5c293457a..fd3b53bae 100644
--- a/sentry-samples/sentry-samples-android/build.gradle.kts
+++ b/sentry-samples/sentry-samples-android/build.gradle.kts
@@ -66,7 +66,8 @@ android {
buildTypes {
getByName("debug") {
manifestPlaceholders = mapOf(
- "sentryDebug" to true
+ "sentryDebug" to true,
+ "sentryEnvironment" to "debug"
)
}
getByName("release") {
@@ -76,7 +77,8 @@ android {
isShrinkResources = true
manifestPlaceholders = mapOf(
- "sentryDebug" to false
+ "sentryDebug" to false,
+ "sentryEnvironment" to "release"
)
}
}
diff --git a/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml b/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml
index dc9962982..6caf842bb 100644
--- a/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml
+++ b/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml
@@ -57,13 +57,13 @@
-
+
-
+
-
+
From 9aad2cbe7b39a6ed26433e6fc43257a93c8045e9 Mon Sep 17 00:00:00 2001
From: Manoel Aranda Neto
Date: Thu, 3 Sep 2020 16:06:36 +0200
Subject: [PATCH 18/28] chore 3.0.0-alpha.1
---
gradle.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gradle.properties b/gradle.properties
index 5ee4f9c38..05166c13b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -9,7 +9,7 @@ android.useAndroidX=true
# Release information
buildVersionCode=20038
-versionName=3.0.0-alpha.1-SNAPSHOT
+versionName=3.0.0-alpha.1
# disable renderscript, it's enabled by default
android.defaults.buildfeatures.renderscript=false
From e767af6215fc459339059c444afe3d4d2e6591f2 Mon Sep 17 00:00:00 2001
From: Manoel Aranda Neto
Date: Thu, 3 Sep 2020 16:07:21 +0200
Subject: [PATCH 19/28] prepare: 3.0.0-alpha.2
---
gradle.properties | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gradle.properties b/gradle.properties
index 05166c13b..cce174a00 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -8,8 +8,8 @@ org.gradle.caching=true
android.useAndroidX=true
# Release information
-buildVersionCode=20038
-versionName=3.0.0-alpha.1
+buildVersionCode=20039
+versionName=3.0.0-alpha.2-SNAPSHOT
# disable renderscript, it's enabled by default
android.defaults.buildfeatures.renderscript=false
From b754303a95e1e59d0262acdf51302441404f844a Mon Sep 17 00:00:00 2001
From: Manoel Aranda Neto
Date: Thu, 3 Sep 2020 16:14:02 +0200
Subject: [PATCH 20/28] Bump Gradle to 6.6.1
---
gradle/wrapper/gradle-wrapper.jar | Bin 58910 -> 59203 bytes
gradle/wrapper/gradle-wrapper.properties | 2 +-
gradlew | 2 +-
gradlew.bat | 21 +++------------------
4 files changed, 5 insertions(+), 20 deletions(-)
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 62d4c053550b91381bbd28b1afc82d634bf73a8a..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644
GIT binary patch
delta 6656
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
Yte!%o?r4GTrVtxD61Ym!|5fq-1K$0e!T1w
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
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 6c9a22477..12d38de6a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index fbd7c5158..4f906e0c8 100755
--- a/gradlew
+++ b/gradlew
@@ -130,7 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
+
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
diff --git a/gradlew.bat b/gradlew.bat
index a9f778a7a..ac1b06f93 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -54,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -64,21 +64,6 @@ echo location of your Java installation.
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
@@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
From 60af521d15ddf06757e43746d67c7b11ee1c0d2e Mon Sep 17 00:00:00 2001
From: Maciej Walkowiak
Date: Fri, 4 Sep 2020 12:09:57 +0200
Subject: [PATCH 21/28] Extract minimum required coverage by Jacoco plugin.
(#541)
---
buildSrc/src/main/java/Config.kt | 6 ++++++
sentry-core/build.gradle.kts | 5 ++---
sentry-log4j2/build.gradle.kts | 4 ++--
sentry-logback/build.gradle.kts | 4 ++--
sentry-spring-boot-starter/build.gradle.kts | 4 ++--
5 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt
index 9246dbb3d..5b2d0f833 100644
--- a/buildSrc/src/main/java/Config.kt
+++ b/buildSrc/src/main/java/Config.kt
@@ -1,3 +1,5 @@
+import java.math.BigDecimal
+
object Config {
val kotlinVersion = "1.3.72"
val kotlinStdLib = "stdlib-jdk8"
@@ -72,6 +74,10 @@ object Config {
}
object QualityPlugins {
+ object Jacoco {
+ val version = "0.8.5"
+ val minimumCoverage = BigDecimal.valueOf(0.6)
+ }
val jacocoVersion = "0.8.5"
val spotless = "com.diffplug.spotless"
val spotlessVersion = "5.1.0"
diff --git a/sentry-core/build.gradle.kts b/sentry-core/build.gradle.kts
index 58bb6468a..389a41d48 100644
--- a/sentry-core/build.gradle.kts
+++ b/sentry-core/build.gradle.kts
@@ -43,7 +43,7 @@ configure {
}
jacoco {
- toolVersion = Config.QualityPlugins.jacocoVersion
+ toolVersion = Config.QualityPlugins.Jacoco.version
}
tasks.jacocoTestReport {
@@ -56,8 +56,7 @@ tasks.jacocoTestReport {
tasks {
jacocoTestCoverageVerification {
violationRules {
- // TODO: Raise the minimum to a sensible value.
- rule { limit { minimum = BigDecimal.valueOf(0.1) } }
+ rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } }
}
}
check {
diff --git a/sentry-log4j2/build.gradle.kts b/sentry-log4j2/build.gradle.kts
index f2c4408c7..65f45d222 100644
--- a/sentry-log4j2/build.gradle.kts
+++ b/sentry-log4j2/build.gradle.kts
@@ -44,7 +44,7 @@ configure {
}
jacoco {
- toolVersion = Config.QualityPlugins.jacocoVersion
+ toolVersion = Config.QualityPlugins.Jacoco.version
}
tasks.jacocoTestReport {
@@ -57,7 +57,7 @@ tasks.jacocoTestReport {
tasks {
jacocoTestCoverageVerification {
violationRules {
- rule { limit { minimum = BigDecimal.valueOf(0.6) } }
+ rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } }
}
}
check {
diff --git a/sentry-logback/build.gradle.kts b/sentry-logback/build.gradle.kts
index 7b42bdd29..92e6c6926 100644
--- a/sentry-logback/build.gradle.kts
+++ b/sentry-logback/build.gradle.kts
@@ -43,7 +43,7 @@ configure {
}
jacoco {
- toolVersion = Config.QualityPlugins.jacocoVersion
+ toolVersion = Config.QualityPlugins.Jacoco.version
}
tasks.jacocoTestReport {
@@ -56,7 +56,7 @@ tasks.jacocoTestReport {
tasks {
jacocoTestCoverageVerification {
violationRules {
- rule { limit { minimum = BigDecimal.valueOf(0.6) } }
+ rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } }
}
}
check {
diff --git a/sentry-spring-boot-starter/build.gradle.kts b/sentry-spring-boot-starter/build.gradle.kts
index 100ea7aee..24e556c71 100644
--- a/sentry-spring-boot-starter/build.gradle.kts
+++ b/sentry-spring-boot-starter/build.gradle.kts
@@ -63,7 +63,7 @@ configure {
}
jacoco {
- toolVersion = Config.QualityPlugins.jacocoVersion
+ toolVersion = Config.QualityPlugins.Jacoco.version
}
tasks.jacocoTestReport {
@@ -76,7 +76,7 @@ tasks.jacocoTestReport {
tasks {
jacocoTestCoverageVerification {
violationRules {
- rule { limit { minimum = BigDecimal.valueOf(0.6) } }
+ rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } }
}
}
check {
From 923f9fc175c0548b13f210c51322672be3cce4ba Mon Sep 17 00:00:00 2001
From: Maciej Walkowiak
Date: Tue, 8 Sep 2020 20:49:10 +0200
Subject: [PATCH 22/28] Add Spring Integration. (#539)
---
buildSrc/src/main/java/Config.kt | 8 +-
.../main/java/io/sentry/core/Breadcrumb.java | 16 ++
...DuplicateEventDetectionEventProcessor.java | 75 ++++++++
.../java/io/sentry/core/SentryOptions.java | 1 +
.../java/io/sentry/core/BreadcrumbTest.kt | 9 +
...plicateEventDetectionEventProcessorTest.kt | 70 ++++++++
.../build.gradle.kts | 11 +-
.../spring/boot/CustomEventProcessor.java | 35 ++++
.../io/sentry/samples/spring/boot/Person.java | 24 +++
.../samples/spring/boot/PersonController.java | 28 +++
.../spring/boot/SecurityConfiguration.java | 60 +++++++
.../{ => boot}/SentryDemoApplication.java | 3 +-
.../src/main/resources/application.properties | 2 +
.../src/main/resources/logback.xml | 4 +-
.../sentry-samples-spring/build.gradle.kts | 41 +++++
.../java/io/sentry/samples/spring/Person.java | 0
.../samples/spring/PersonController.java | 1 +
.../samples/spring/SecurityConfiguration.java | 2 +-
.../samples/spring/SentryDemoApplication.java | 17 ++
.../src/main/resources/logback.xml | 14 ++
sentry-spring-boot-starter/build.gradle.kts | 7 +-
.../spring/boot/SentryAutoConfiguration.java | 26 +--
.../boot/SentrySpringIntegrationTest.kt | 27 ++-
sentry-spring/build.gradle.kts | 118 +++++++++++++
.../java/io/sentry/spring/EnableSentry.java | 32 ++++
.../spring/SentryExceptionResolver.java | 55 ++++++
.../io/sentry/spring/SentryHubRegistrar.java | 73 ++++++++
.../spring/SentryInitBeanPostProcessor.java | 21 +++
.../sentry/spring}/SentryRequestFilter.java | 17 +-
...tryRequestHttpServletRequestProcessor.java | 7 +-
.../sentry/spring}/SentrySecurityFilter.java | 7 +-
...SentryUserHttpServletRequestProcessor.java | 2 +-
.../sentry/spring/SentryWebConfiguration.java | 25 +++
.../io/sentry/spring/EnableSentryTest.kt | 78 +++++++++
...yRequestHttpServletRequestProcessorTest.kt | 2 +-
.../spring/SentrySpringIntegrationTest.kt | 163 ++++++++++++++++++
...ntryUserHttpServletRequestProcessorTest.kt | 2 +-
settings.gradle.kts | 2 +
38 files changed, 1036 insertions(+), 49 deletions(-)
create mode 100644 sentry-core/src/main/java/io/sentry/core/DuplicateEventDetectionEventProcessor.java
create mode 100644 sentry-core/src/test/java/io/sentry/core/DuplicateEventDetectionEventProcessorTest.kt
create mode 100644 sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java
create mode 100644 sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/Person.java
create mode 100644 sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/PersonController.java
create mode 100644 sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/SecurityConfiguration.java
rename sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/{ => boot}/SentryDemoApplication.java (88%)
create mode 100644 sentry-samples/sentry-samples-spring/build.gradle.kts
rename sentry-samples/{sentry-samples-spring-boot => sentry-samples-spring}/src/main/java/io/sentry/samples/spring/Person.java (100%)
rename sentry-samples/{sentry-samples-spring-boot => sentry-samples-spring}/src/main/java/io/sentry/samples/spring/PersonController.java (94%)
rename sentry-samples/{sentry-samples-spring-boot => sentry-samples-spring}/src/main/java/io/sentry/samples/spring/SecurityConfiguration.java (97%)
create mode 100644 sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/SentryDemoApplication.java
create mode 100644 sentry-samples/sentry-samples-spring/src/main/resources/logback.xml
create mode 100644 sentry-spring/build.gradle.kts
create mode 100644 sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java
create mode 100644 sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java
create mode 100644 sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java
create mode 100644 sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java
rename {sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot => sentry-spring/src/main/java/io/sentry/spring}/SentryRequestFilter.java (68%)
rename {sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot => sentry-spring/src/main/java/io/sentry/spring}/SentryRequestHttpServletRequestProcessor.java (92%)
rename {sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot => sentry-spring/src/main/java/io/sentry/spring}/SentrySecurityFilter.java (90%)
rename {sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot => sentry-spring/src/main/java/io/sentry/spring}/SentryUserHttpServletRequestProcessor.java (97%)
create mode 100644 sentry-spring/src/main/java/io/sentry/spring/SentryWebConfiguration.java
create mode 100644 sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt
rename {sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot => sentry-spring/src/test/kotlin/io/sentry/spring}/SentryRequestHttpServletRequestProcessorTest.kt (99%)
create mode 100644 sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringIntegrationTest.kt
rename {sentry-spring-boot-starter/src/test/kotlin/io/sentry/spring/boot => sentry-spring/src/test/kotlin/io/sentry/spring}/SentryUserHttpServletRequestProcessorTest.kt (98%)
diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt
index 5b2d0f833..766b91e89 100644
--- a/buildSrc/src/main/java/Config.kt
+++ b/buildSrc/src/main/java/Config.kt
@@ -48,6 +48,9 @@ object Config {
val log4j2Core = "org.apache.logging.log4j:log4j-core:$log4j2Version"
val springBootStarter = "org.springframework.boot:spring-boot-starter:$springBootVersion"
+ val springBootStarterTest = "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
+ val springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
+ val springBootStarterSecurity = "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
val springWeb = "org.springframework:spring-webmvc"
val servletApi = "javax.servlet:javax.servlet-api"
@@ -68,9 +71,6 @@ object Config {
val robolectric = "org.robolectric:robolectric:4.3.1"
val mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
val awaitility = "org.awaitility:awaitility-kotlin:4.0.3"
- val springBootStarterTest = "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
- val springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
- val springBootStarterSecurity = "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
}
object QualityPlugins {
@@ -78,7 +78,6 @@ object Config {
val version = "0.8.5"
val minimumCoverage = BigDecimal.valueOf(0.6)
}
- val jacocoVersion = "0.8.5"
val spotless = "com.diffplug.spotless"
val spotlessVersion = "5.1.0"
val errorProne = "net.ltgt.errorprone"
@@ -95,6 +94,7 @@ object Config {
val SENTRY_ANDROID_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.android"
val SENTRY_LOGBACK_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.logback"
val SENTRY_LOG4J2_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.log4j2"
+ val SENTRY_SPRING_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring"
val SENTRY_SPRING_BOOT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot"
val group = "io.sentry"
val description = "SDK for sentry.io"
diff --git a/sentry-core/src/main/java/io/sentry/core/Breadcrumb.java b/sentry-core/src/main/java/io/sentry/core/Breadcrumb.java
index cfe7c8f35..d990e4daf 100644
--- a/sentry-core/src/main/java/io/sentry/core/Breadcrumb.java
+++ b/sentry-core/src/main/java/io/sentry/core/Breadcrumb.java
@@ -43,6 +43,22 @@ public final class Breadcrumb implements Cloneable, IUnknownPropertiesConsumer {
this.timestamp = timestamp;
}
+ /**
+ * Creates HTTP breadcrumb.
+ *
+ * @param url - the request URL
+ * @param method - the request method
+ * @return the breadcrumb
+ */
+ public static @NotNull Breadcrumb http(final @NotNull String url, final @NotNull String method) {
+ final Breadcrumb breadcrumb = new Breadcrumb();
+ breadcrumb.setType("http");
+ breadcrumb.setCategory("http");
+ breadcrumb.setData("url", url);
+ breadcrumb.setData("method", method.toUpperCase(Locale.getDefault()));
+ return breadcrumb;
+ }
+
/** Breadcrumb ctor */
public Breadcrumb() {
this(DateUtils.getCurrentDateTimeOrNull());
diff --git a/sentry-core/src/main/java/io/sentry/core/DuplicateEventDetectionEventProcessor.java b/sentry-core/src/main/java/io/sentry/core/DuplicateEventDetectionEventProcessor.java
new file mode 100644
index 000000000..e62824128
--- /dev/null
+++ b/sentry-core/src/main/java/io/sentry/core/DuplicateEventDetectionEventProcessor.java
@@ -0,0 +1,75 @@
+package io.sentry.core;
+
+import io.sentry.core.exception.ExceptionMechanismException;
+import io.sentry.core.util.Objects;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/** Deduplicates events containing throwable that has been already processed. */
+public final class DuplicateEventDetectionEventProcessor implements EventProcessor {
+ private final WeakHashMap capturedObjects = new WeakHashMap<>();
+ private final SentryOptions options;
+
+ public DuplicateEventDetectionEventProcessor(final @NotNull SentryOptions options) {
+ this.options = Objects.requireNonNull(options, "options are required");
+ }
+
+ @Override
+ public SentryEvent process(final @NotNull SentryEvent event, final @Nullable Object hint) {
+ final Throwable throwable = event.getThrowable();
+ if (throwable != null) {
+ if (throwable instanceof ExceptionMechanismException) {
+ final ExceptionMechanismException ex = (ExceptionMechanismException) throwable;
+ if (capturedObjects.containsKey(ex.getThrowable())) {
+ options
+ .getLogger()
+ .log(
+ SentryLevel.DEBUG,
+ "Duplicate Exception detected. Event %s will be discarded.",
+ event.getEventId());
+ return null;
+ } else {
+ capturedObjects.put(ex.getThrowable(), null);
+ }
+ } else {
+ if (capturedObjects.containsKey(throwable)
+ || containsAnyKey(capturedObjects, allCauses(throwable))) {
+ options
+ .getLogger()
+ .log(
+ SentryLevel.DEBUG,
+ "Duplicate Exception detected. Event %s will be discarded.",
+ event.getEventId());
+ return null;
+ } else {
+ capturedObjects.put(throwable, null);
+ }
+ }
+ }
+ return event;
+ }
+
+ private static boolean containsAnyKey(
+ final @NotNull Map map, final @NotNull List list) {
+ for (T entry : list) {
+ if (map.containsKey(entry)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static @NotNull List allCauses(final @NotNull Throwable throwable) {
+ final List causes = new ArrayList<>();
+ Throwable ex = throwable;
+ while (ex.getCause() != null) {
+ causes.add(ex.getCause());
+ ex = ex.getCause();
+ }
+ return causes;
+ }
+}
diff --git a/sentry-core/src/main/java/io/sentry/core/SentryOptions.java b/sentry-core/src/main/java/io/sentry/core/SentryOptions.java
index 7c10b1702..21cbb10b5 100644
--- a/sentry-core/src/main/java/io/sentry/core/SentryOptions.java
+++ b/sentry-core/src/main/java/io/sentry/core/SentryOptions.java
@@ -1047,6 +1047,7 @@ public SentryOptions() {
integrations.add(new ShutdownHookIntegration());
eventProcessors.add(new MainEventProcessor(this));
+ eventProcessors.add(new DuplicateEventDetectionEventProcessor(this));
setSentryClientName(BuildConfig.SENTRY_JAVA_SDK_NAME + "/" + BuildConfig.VERSION_NAME);
setSdkVersion(createSdkVersion());
diff --git a/sentry-core/src/test/java/io/sentry/core/BreadcrumbTest.kt b/sentry-core/src/test/java/io/sentry/core/BreadcrumbTest.kt
index d38ef8966..f7a2a9258 100644
--- a/sentry-core/src/test/java/io/sentry/core/BreadcrumbTest.kt
+++ b/sentry-core/src/test/java/io/sentry/core/BreadcrumbTest.kt
@@ -98,4 +98,13 @@ class BreadcrumbTest {
val breadcrumb = Breadcrumb("this is a test")
assertEquals("this is a test", breadcrumb.message)
}
+
+ @Test
+ fun `creates HTTP breadcrumb`() {
+ val breadcrumb = Breadcrumb.http("http://example.com", "POST")
+ assertEquals("http://example.com", breadcrumb.data["url"])
+ assertEquals("POST", breadcrumb.data["method"])
+ assertEquals("http", breadcrumb.type)
+ assertEquals("http", breadcrumb.category)
+ }
}
diff --git a/sentry-core/src/test/java/io/sentry/core/DuplicateEventDetectionEventProcessorTest.kt b/sentry-core/src/test/java/io/sentry/core/DuplicateEventDetectionEventProcessorTest.kt
new file mode 100644
index 000000000..3867fd5f5
--- /dev/null
+++ b/sentry-core/src/test/java/io/sentry/core/DuplicateEventDetectionEventProcessorTest.kt
@@ -0,0 +1,70 @@
+package io.sentry.core
+
+import io.sentry.core.exception.ExceptionMechanismException
+import io.sentry.core.protocol.Mechanism
+import java.lang.RuntimeException
+import kotlin.test.Test
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+
+class DuplicateEventDetectionEventProcessorTest {
+
+ val processor = DuplicateEventDetectionEventProcessor(SentryOptions())
+
+ @Test
+ fun `does not drop event if no previous event with same exception was processed`() {
+ processor.process(SentryEvent(), null)
+
+ val result = processor.process(SentryEvent(RuntimeException()), null)
+
+ assertNotNull(result)
+ }
+
+ @Test
+ fun `drops event with the same exception`() {
+ val event = SentryEvent(RuntimeException())
+ processor.process(event, null)
+
+ val result = processor.process(event, null)
+ assertNull(result)
+ }
+
+ @Test
+ fun `drops event with mechanism exception having an exception that has already been processed`() {
+ val event = SentryEvent(RuntimeException())
+ processor.process(event, null)
+
+ val result = processor.process(SentryEvent(ExceptionMechanismException(Mechanism(), event.throwable, null)), null)
+ assertNull(result)
+ }
+
+ @Test
+ fun `drops event with exception that has already been processed with event with mechanism exception`() {
+ val sentryEvent = SentryEvent(ExceptionMechanismException(Mechanism(), RuntimeException(), null))
+ processor.process(sentryEvent, null)
+
+ val result = processor.process(SentryEvent((sentryEvent.throwable as ExceptionMechanismException).throwable), null)
+
+ assertNull(result)
+ }
+
+ @Test
+ fun `drops event with the cause equal to exception in already processed event`() {
+ val event = SentryEvent(RuntimeException())
+ processor.process(event, null)
+
+ val result = processor.process(SentryEvent(RuntimeException(event.throwable)), null)
+
+ assertNull(result)
+ }
+
+ @Test
+ fun `drops event with any of the causes has been already processed`() {
+ val event = SentryEvent(RuntimeException())
+ processor.process(event, null)
+
+ val result = processor.process(SentryEvent(RuntimeException(RuntimeException(event.throwable))), null)
+
+ assertNull(result)
+ }
+}
diff --git a/sentry-samples/sentry-samples-spring-boot/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot/build.gradle.kts
index 9676cdb87..e342bf150 100644
--- a/sentry-samples/sentry-samples-spring-boot/build.gradle.kts
+++ b/sentry-samples/sentry-samples-spring-boot/build.gradle.kts
@@ -1,3 +1,4 @@
+import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
@@ -16,14 +17,14 @@ repositories {
}
dependencies {
- implementation("org.springframework.boot:spring-boot-starter-security")
- implementation("org.springframework.boot:spring-boot-starter-web")
- implementation("org.springframework.boot:spring-boot-starter")
+ implementation(Config.Libs.springBootStarterSecurity)
+ implementation(Config.Libs.springBootStarterWeb)
+ implementation(Config.Libs.springBootStarter)
implementation("org.jetbrains.kotlin:kotlin-reflect")
- implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
+ implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))
implementation(project(":sentry-spring-boot-starter"))
implementation(project(":sentry-logback"))
- testImplementation("org.springframework.boot:spring-boot-starter-test") {
+ testImplementation(Config.Libs.springBootStarterTest) {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
}
diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java
new file mode 100644
index 000000000..40d17a1c6
--- /dev/null
+++ b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/CustomEventProcessor.java
@@ -0,0 +1,35 @@
+package io.sentry.samples.spring.boot;
+
+import io.sentry.core.EventProcessor;
+import io.sentry.core.SentryEvent;
+import io.sentry.core.protocol.SentryRuntime;
+import org.jetbrains.annotations.Nullable;
+import org.springframework.stereotype.Component;
+
+/**
+ * Custom {@link EventProcessor} implementation lets modifying {@link SentryEvent}s before they are
+ * sent to Sentry.
+ */
+@Component
+public class CustomEventProcessor implements EventProcessor {
+ private final String javaVersion;
+ private final String javaVendor;
+
+ public CustomEventProcessor(String javaVersion, String javaVendor) {
+ this.javaVersion = javaVersion;
+ this.javaVendor = javaVendor;
+ }
+
+ public CustomEventProcessor() {
+ this(System.getProperty("java.version"), System.getProperty("java.vendor"));
+ }
+
+ @Override
+ public SentryEvent process(SentryEvent event, @Nullable Object hint) {
+ final SentryRuntime runtime = new SentryRuntime();
+ runtime.setVersion(javaVersion);
+ runtime.setName(javaVendor);
+ event.getContexts().setRuntime(runtime);
+ return event;
+ }
+}
diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/Person.java b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/Person.java
new file mode 100644
index 000000000..2a2177d46
--- /dev/null
+++ b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/Person.java
@@ -0,0 +1,24 @@
+package io.sentry.samples.spring.boot;
+
+public class Person {
+ private final String firstName;
+ private final String lastName;
+
+ public Person(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ @Override
+ public String toString() {
+ return "Person{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + '}';
+ }
+}
diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/PersonController.java b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/PersonController.java
new file mode 100644
index 000000000..7ee2e4604
--- /dev/null
+++ b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/PersonController.java
@@ -0,0 +1,28 @@
+package io.sentry.samples.spring.boot;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/person/")
+public class PersonController {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PersonController.class);
+
+ @GetMapping("{id}")
+ Person person(@PathVariable Long id) {
+ LOGGER.info("Loading person with id={}", id);
+ throw new IllegalArgumentException("Something went wrong [id=" + id + "]");
+ }
+
+ @PostMapping
+ Person create(@RequestBody Person person) {
+ LOGGER.warn("Creating person: {}", person);
+ return person;
+ }
+}
diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/SecurityConfiguration.java b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/SecurityConfiguration.java
new file mode 100644
index 000000000..417f0541d
--- /dev/null
+++ b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/SecurityConfiguration.java
@@ -0,0 +1,60 @@
+package io.sentry.samples.spring.boot;
+
+import io.sentry.core.IHub;
+import io.sentry.core.SentryOptions;
+import io.sentry.spring.SentrySecurityFilter;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+
+@Configuration
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ private final @NotNull IHub hub;
+ private final @NotNull SentryOptions options;
+
+ public SecurityConfiguration(final @NotNull IHub hub, final @NotNull SentryOptions options) {
+ this.hub = hub;
+ this.options = options;
+ }
+
+ // this API is meant to be consumed by non-browser clients thus the CSRF protection is not needed.
+ @Override
+ @SuppressWarnings("lgtm[java/spring-disabled-csrf-protection]")
+ protected void configure(final @NotNull HttpSecurity http) throws Exception {
+ // register SentrySecurityFilter to attach user information to SentryEvents
+ http.addFilterAfter(new SentrySecurityFilter(hub, options), AnonymousAuthenticationFilter.class)
+ .csrf()
+ .disable()
+ .authorizeRequests()
+ .anyRequest()
+ .authenticated()
+ .and()
+ .httpBasic();
+ }
+
+ @Bean
+ @Override
+ public @NotNull UserDetailsService userDetailsService() {
+ final PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+
+ final UserDetails user =
+ User.builder()
+ .passwordEncoder(encoder::encode)
+ .username("user")
+ .password("password")
+ .roles("USER")
+ .build();
+
+ return new InMemoryUserDetailsManager(user);
+ }
+}
diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/SentryDemoApplication.java b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/SentryDemoApplication.java
similarity index 88%
rename from sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/SentryDemoApplication.java
rename to sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/SentryDemoApplication.java
index 43882fe6c..14e57e0cc 100644
--- a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/SentryDemoApplication.java
+++ b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/SentryDemoApplication.java
@@ -1,11 +1,10 @@
-package io.sentry.samples.spring;
+package io.sentry.samples.spring.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SentryDemoApplication {
-
public static void main(String[] args) {
SpringApplication.run(SentryDemoApplication.class, args);
}
diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties
index 9f28892b0..84b7ca93c 100644
--- a/sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties
+++ b/sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties
@@ -1,3 +1,5 @@
# NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in your Sentry project/dashboard
sentry.dsn=https://f7f320d5c3a54709be7b28e0f2ca7081@sentry.io/1808954
sentry.send-default-pii=true
+# Sentry Spring Boot integration allows more fine-grained SentryOptions configuration
+sentry.max-breadcrumbs=150
diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/resources/logback.xml b/sentry-samples/sentry-samples-spring-boot/src/main/resources/logback.xml
index fe050c21c..26c88f349 100644
--- a/sentry-samples/sentry-samples-spring-boot/src/main/resources/logback.xml
+++ b/sentry-samples/sentry-samples-spring-boot/src/main/resources/logback.xml
@@ -4,9 +4,7 @@
-
-