Skip to content

Commit 8a8d947

Browse files
runningcodeclaude
andauthored
fix(core): Clear contexts on Scope.clear() (#5902)
* fix(core): Clear contexts on Scope.clear() (JAVA-683) Scope.clear() reset every field except contexts, so anything set via setContexts survived a clear. Contexts had no clear() method, unlike the tags, extra, and fingerprint collections, which is why it was skipped. Add Contexts.clear(), override it in CombinedContextsView to delegate to the default context, and clear contexts in Scope.clear() while notifying scope observers so persisted scopes stay in sync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * changelog --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 7d649bd commit 8a8d947

8 files changed

Lines changed: 60 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixes
66

7+
- Clear contexts when calling `Scope.clear()` ([#5902](https://github.com/getsentry/sentry-java/pull/5902))
78
- Preserve custom `Throwable` identities when R8 optimizes Android apps ([#5881](https://github.com/getsentry/sentry-java/pull/5881))
89

910
## 8.52.0

‎sentry/api/sentry.api‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ public final class io/sentry/CheckInStatus : java/lang/Enum {
227227

228228
public final class io/sentry/CombinedContextsView : io/sentry/protocol/Contexts {
229229
public fun <init> (Lio/sentry/protocol/Contexts;Lio/sentry/protocol/Contexts;Lio/sentry/protocol/Contexts;Lio/sentry/ScopeType;)V
230+
public fun clear ()V
230231
public fun containsKey (Ljava/lang/Object;)Z
231232
public fun entrySet ()Ljava/util/Set;
232233
public fun get (Ljava/lang/Object;)Ljava/lang/Object;
@@ -5795,6 +5796,7 @@ public class io/sentry/protocol/Contexts : io/sentry/JsonSerializable {
57955796
protected final field responseLock Lio/sentry/util/AutoClosableReentrantLock;
57965797
public fun <init> ()V
57975798
public fun <init> (Lio/sentry/protocol/Contexts;)V
5799+
public fun clear ()V
57985800
public fun containsKey (Ljava/lang/Object;)Z
57995801
public fun entrySet ()Ljava/util/Set;
58005802
public fun equals (Ljava/lang/Object;)Z

‎sentry/src/main/java/io/sentry/CombinedContextsView.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,11 @@ public boolean containsKey(final @Nullable Object key) {
293293
return getDefaultContexts().remove(key);
294294
}
295295

296+
@Override
297+
public void clear() {
298+
getDefaultContexts().clear();
299+
}
300+
296301
@Override
297302
public @NotNull Enumeration<String> keys() {
298303
return mergeContexts().keys();

‎sentry/src/main/java/io/sentry/Scope.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,10 +575,15 @@ public void clear() {
575575
tags.clear();
576576
attributes.clear();
577577
extra.clear();
578+
contexts.clear();
578579
eventProcessors.clear();
579580
clearTransaction();
580581
clearAttachments();
581582
clearFeatureFlags();
583+
584+
for (final IScopeObserver observer : options.getScopeObservers()) {
585+
observer.setContexts(contexts);
586+
}
582587
}
583588

584589
/**

‎sentry/src/main/java/io/sentry/protocol/Contexts.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ public boolean containsKey(final @Nullable Object key) {
248248
return internalStorage.remove(key);
249249
}
250250

251+
public void clear() {
252+
internalStorage.clear();
253+
}
254+
251255
public @NotNull Enumeration<String> keys() {
252256
return internalStorage.keys();
253257
}

‎sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,20 @@ class CombinedContextsViewTest {
605605
assertEquals("global", fixture.global.get("test"))
606606
}
607607

608+
@Test
609+
fun `clear clears default context only`() {
610+
val combined = fixture.getSut()
611+
fixture.current.put("test", "current")
612+
fixture.isolation.put("test", "isolation")
613+
fixture.global.put("test", "global")
614+
615+
combined.clear()
616+
617+
assertEquals("current", fixture.current.get("test"))
618+
assertNull(fixture.isolation.get("test"))
619+
assertEquals("global", fixture.global.get("test"))
620+
}
621+
608622
@Test
609623
fun `set null value on context does not cause exception`() {
610624
val combined = fixture.getSut()

‎sentry/src/test/java/io/sentry/ScopeTest.kt‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ class ScopeTest {
292292
scope.screen = "MainActivity"
293293
scope.setExtra("some", "extra")
294294
scope.setAttribute("some", "attribute")
295+
scope.setContexts("some", "context")
295296
scope.addEventProcessor(eventProcessor())
296297
scope.addAttachment(Attachment("path"))
297298
scope.addFeatureFlag("flag", true)
@@ -308,6 +309,7 @@ class ScopeTest {
308309
assertEquals(0, scope.tags.size)
309310
assertEquals(0, scope.attributes.size)
310311
assertEquals(0, scope.extras.size)
312+
assertTrue(scope.contexts.isEmpty)
311313
assertEquals(0, scope.eventProcessors.size)
312314
assertEquals(0, scope.attachments.size)
313315
assertEquals(0, scope.featureFlags!!.values.size)
@@ -886,6 +888,21 @@ class ScopeTest {
886888
verify(observer).setContexts(argThat { (get("test") as Obj).stuff == 3 })
887889
}
888890

891+
@Test
892+
fun `Scope clear contexts sync scopes`() {
893+
val observer = mock<IScopeObserver>()
894+
val options = SentryOptions().apply { addScopeObserver(observer) }
895+
val scope = Scope(options)
896+
897+
// Populate the live contexts directly so the observer is only notified by clear().
898+
scope.contexts.set("test", "value")
899+
assertFalse(scope.contexts.isEmpty)
900+
901+
scope.clear()
902+
assertTrue(scope.contexts.isEmpty)
903+
verify(observer).setContexts(argThat { isEmpty })
904+
}
905+
889906
@Test
890907
fun `Scope set propagation context sync scopes`() {
891908
val observer = mock<IScopeObserver>()

‎sentry/src/test/java/io/sentry/protocol/ContextsTest.kt‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import kotlin.test.assertEquals
77
import kotlin.test.assertFalse
88
import kotlin.test.assertNotNull
99
import kotlin.test.assertNotSame
10+
import kotlin.test.assertTrue
1011

1112
class ContextsTest {
1213
@Test
@@ -137,4 +138,15 @@ class ContextsTest {
137138

138139
assertEquals(listOf("a"), contexts.keys().toList())
139140
}
141+
142+
@Test
143+
fun `clear removes all entries from context`() {
144+
val contexts = Contexts()
145+
contexts.put("a", "1")
146+
contexts.put("b", "2")
147+
148+
contexts.clear()
149+
150+
assertTrue(contexts.isEmpty)
151+
}
140152
}

0 commit comments

Comments
 (0)