@@ -18,6 +18,7 @@ package okhttp3.internal
1818import assertk.assertThat
1919import assertk.assertions.isEqualTo
2020import assertk.assertions.isNull
21+ import java.util.concurrent.atomic.AtomicReference
2122import org.junit.jupiter.api.Test
2223
2324class TagsTest {
@@ -157,4 +158,67 @@ class TagsTest {
157158 assertThat(tags[String ::class ]).isEqualTo(" a" )
158159 assertThat(tags.toString()).isEqualTo(" {class kotlin.String=a}" )
159160 }
161+
162+ @Test
163+ fun computeIfAbsentWhenEmpty () {
164+ val tags = EmptyTags
165+ val atomicTags = AtomicReference <Tags >(tags)
166+ assertThat(atomicTags.computeIfAbsent(String ::class ) { " a" }).isEqualTo(" a" )
167+ assertThat(atomicTags.get()[String ::class ]).isEqualTo(" a" )
168+ }
169+
170+ @Test
171+ fun computeIfAbsentWhenPresent () {
172+ val tags = EmptyTags .plus(String ::class , " a" )
173+ val atomicTags = AtomicReference (tags)
174+ assertThat(atomicTags.computeIfAbsent(String ::class ) { " b" }).isEqualTo(" a" )
175+ assertThat(atomicTags.get()[String ::class ]).isEqualTo(" a" )
176+ }
177+
178+ @Test
179+ fun computeIfAbsentWhenDifferentKeyRaceLostDuringCompute () {
180+ val tags = EmptyTags
181+ val atomicTags = AtomicReference <Tags >(tags)
182+ val result =
183+ atomicTags.computeIfAbsent(String ::class ) {
184+ // 'Race' by making another computeIfAbsent call. In practice this would be another thread.
185+ assertThat(atomicTags.computeIfAbsent(Integer ::class ) { 5 as Integer }).isEqualTo(5 )
186+ " a"
187+ }
188+ assertThat(result).isEqualTo(" a" )
189+ assertThat(atomicTags.get()[String ::class ]).isEqualTo(" a" )
190+ assertThat(atomicTags.get()[Integer ::class ]).isEqualTo(5 )
191+ }
192+
193+ @Test
194+ fun computeIfAbsentWhenSameKeyRaceLostDuringCompute () {
195+ val tags = EmptyTags
196+ val atomicTags = AtomicReference <Tags >(tags)
197+ val result =
198+ atomicTags.computeIfAbsent(String ::class ) {
199+ // 'Race' by making another computeIfAbsent call. In practice this would be another thread.
200+ assertThat(atomicTags.computeIfAbsent(String ::class ) { " b" }).isEqualTo(" b" )
201+ " a"
202+ }
203+ assertThat(result).isEqualTo(" b" )
204+ assertThat(atomicTags.get()[String ::class ]).isEqualTo(" b" )
205+ }
206+
207+ @Test
208+ fun computeIfAbsentOnlyComputesOnceAfterRaceLost () {
209+ var computeCount = 0
210+ val tags = EmptyTags
211+ val atomicTags = AtomicReference <Tags >(tags)
212+ val result =
213+ atomicTags.computeIfAbsent(String ::class ) {
214+ computeCount++
215+ // 'Race' by making another computeIfAbsent call. In practice this would be another thread.
216+ assertThat(atomicTags.computeIfAbsent(Integer ::class ) { 5 as Integer }).isEqualTo(5 )
217+ " a"
218+ }
219+ assertThat(result).isEqualTo(" a" )
220+ assertThat(computeCount).isEqualTo(1 )
221+ assertThat(atomicTags.get()[Integer ::class ]).isEqualTo(5 )
222+ assertThat(atomicTags.get()[String ::class ]).isEqualTo(" a" )
223+ }
160224}
0 commit comments