View Javadoc

1   package com.atlassian.vcache.internal.test;
2   
3   import com.atlassian.utt.concurrency.Barrier;
4   import com.atlassian.vcache.LocalCacheOperations;
5   import org.junit.Before;
6   import org.junit.Rule;
7   import org.junit.Test;
8   import org.junit.rules.ExpectedException;
9   import org.slf4j.Logger;
10  import org.slf4j.LoggerFactory;
11  
12  import java.time.Duration;
13  import java.util.Map;
14  import java.util.Optional;
15  import java.util.concurrent.CompletableFuture;
16  import java.util.stream.Collectors;
17  
18  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successfulWith;
19  import static com.atlassian.vcache.internal.test.TestUtils.runAndWaitForStart;
20  import static org.hamcrest.Matchers.contains;
21  import static org.hamcrest.Matchers.containsInAnyOrder;
22  import static org.hamcrest.Matchers.empty;
23  import static org.hamcrest.Matchers.hasEntry;
24  import static org.hamcrest.Matchers.is;
25  import static org.hamcrest.Matchers.notNullValue;
26  import static org.junit.Assert.assertThat;
27  
28  /**
29   * Base test class for the {@link LocalCacheOperations}.
30   */
31  public abstract class AbstractLocalCacheOperationsTest {
32      private static final Logger log = LoggerFactory.getLogger(AbstractLocalCacheOperationsTest.class);
33  
34      @Rule
35      public ExpectedException thrown = ExpectedException.none();
36  
37      private LocalCacheOperations<String, String> cache;
38  
39      protected abstract <K, V> LocalCacheOperations<K, V> createCache(String name, Duration lockTimeout);
40  
41      @SuppressWarnings("checkstyle:MagicNumber")
42      protected <K, V> LocalCacheOperations<K, V> createCache(String name) {
43          return createCache(name, Duration.ofSeconds(10));
44      }
45  
46      @Before
47      public void init() {
48          cache = createCache("some.smart.name");
49      }
50  
51      @Test
52      public void testGet() throws Exception {
53          final Optional<String> result1 = cache.get("Parlez-Vouis Francais");
54  
55          assertThat(result1, notNullValue());
56          assertThat(result1, is(Optional.empty()));
57  
58          final String supplied = cache.get("Parlez-Vouis Francais", () -> "Magic Fountain");
59  
60          assertThat(supplied, is("Magic Fountain"));
61  
62          final Optional<String> result2 = cache.get("Parlez-Vouis Francais");
63  
64          assertThat(result2, notNullValue());
65          assertThat(result2, is(Optional.of("Magic Fountain")));
66      }
67  
68      @Test
69      public void testPut() throws Exception {
70          final Optional<String> get1 = cache.get("aqua");
71  
72          assertThat(get1, notNullValue());
73          assertThat(get1, is(Optional.empty()));
74  
75          cache.put("aqua", "water");
76          final Optional<String> put1 = cache.get("aqua");
77  
78          assertThat(put1, notNullValue());
79          assertThat(put1, is(Optional.of("water")));
80      }
81  
82      @Test
83      public void testPutIfAbsent() throws Exception {
84          final Optional<String> get1 = cache.get("aqua");
85  
86          assertThat(get1, notNullValue());
87          assertThat(get1, is(Optional.empty()));
88  
89          final Optional<String> put1 = cache.putIfAbsent("aqua", "water");
90  
91          assertThat(put1, notNullValue());
92          assertThat(put1, is(Optional.empty()));
93  
94          final Optional<String> put2 = cache.putIfAbsent("aqua", "water2");
95  
96          assertThat(put2, notNullValue());
97          assertThat(put2, is(Optional.of("water")));
98  
99          final Optional<String> get2 = cache.get("aqua");
100 
101         assertThat(get2, notNullValue());
102         assertThat(get2, is(Optional.of("water")));
103     }
104 
105     @Test
106     public void testReplaceIfOkay() throws Exception {
107         cache.put("aqua", "water");
108         final boolean replaced = cache.replaceIf("aqua", "water", "water2");
109 
110         assertThat(replaced, is(true));
111         assertThat(cache.get("aqua"), is(Optional.of("water2")));
112     }
113 
114     @Test
115     public void testReplaceIfMismatch() throws Exception {
116         cache.put("aqua", "water");
117         final boolean replaced = cache.replaceIf("aqua", "tea", "water2");
118 
119         assertThat(replaced, is(false));
120         assertThat(cache.get("aqua"), is(Optional.of("water")));
121     }
122 
123     @Test
124     public void testReplaceIfMissing() throws Exception {
125         final boolean replaced = cache.replaceIf("aqua", "water", "water2");
126 
127         assertThat(replaced, is(false));
128         assertThat(cache.get("aqua"), is(Optional.empty()));
129     }
130 
131     @Test
132     public void testRemoveIfOkay() throws Exception {
133         cache.put("aqua", "water");
134 
135         final boolean removed = cache.removeIf("aqua", "water");
136 
137         assertThat(removed, is(true));
138         assertThat(cache.get("aqua"), is(Optional.empty()));
139     }
140 
141     @Test
142     public void testRemoveIfMismatch() throws Exception {
143         cache.put("aqua", "water");
144 
145         final boolean removed = cache.removeIf("aqua", "tea");
146 
147         assertThat(removed, is(false));
148         assertThat(cache.get("aqua"), is(Optional.of("water")));
149     }
150 
151     @Test
152     public void testRemoveIfMissing() throws Exception {
153         final boolean removed = cache.removeIf("aqua", "water");
154 
155         assertThat(removed, is(false));
156         assertThat(cache.get("aqua"), is(Optional.empty()));
157     }
158 
159 
160     @Test
161     public void testRemoveOkay() throws Exception {
162         cache.put("aqua", "water");
163 
164         cache.remove("aqua");
165 
166         assertThat(cache.get("aqua"), is(Optional.empty()));
167     }
168 
169     @Test
170     public void testRemoveMissing() throws Exception {
171         cache.remove("aqua");
172 
173         assertThat(cache.get("aqua"), is(Optional.empty()));
174     }
175 
176     @Test
177     public void testRemoveAll() throws Exception {
178         cache.put("k1", "v1");
179         cache.put("k2", "v2");
180 
181         assertThat(cache.get("k1"), is(Optional.of("v1")));
182         assertThat(cache.get("k2"), is(Optional.of("v2")));
183 
184         cache.removeAll();
185 
186         assertThat(cache.get("k1"), is(Optional.empty()));
187         assertThat(cache.get("k2"), is(Optional.empty()));
188     }
189 
190     @Test
191     public void testInvalidName() {
192         thrown.expect(IllegalArgumentException.class);
193         thrown.expectMessage("Invalid cache name: illegal%name for !@#");
194 
195         cache = createCache("illegal%name for !@#");
196     }
197 
198     @Test
199     public void getBulkFactory() {
200         final Map<String, String> get1 = cache.getBulk(
201                 strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-1")));
202 
203         assertThat(get1.keySet(), empty());
204         assertThat(get1.values(), empty());
205 
206         final Map<String, String> get2 = cache.getBulk(
207                 strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-1")),
208                 "claira", "josie", "josie");
209 
210         assertThat(get2.keySet(), containsInAnyOrder("claira", "josie"));
211         assertThat(get2.values(), containsInAnyOrder("claira-1", "josie-1"));
212 
213         final Map<String, String> get3 = cache.getBulk(
214                 strings -> strings.stream().collect(Collectors.toMap(k -> k, k -> k + "-2")),
215                 "claira", "josie", "jasmin");
216 
217         assertThat(get3.keySet(), containsInAnyOrder("claira", "josie", "jasmin"));
218         assertThat(get3.values(), containsInAnyOrder("claira-1", "josie-1", "jasmin-2"));
219     }
220 
221     @Test
222     public void handle_legal_recursion_supplier() {
223         final String result = cache.get("first", () ->
224                 cache.get("second", () -> "pass"));
225 
226         assertThat(result, is("pass"));
227     }
228 
229     @Test
230     public void deal_with_deadlock_scenario() {
231         // T1: get with supplier and await signal
232         // T2: get with supplier that returns "T2"
233         // T1: signalled, supplier returns "T1"
234         // Confirm both T1 & T2 return "T1"
235 
236         final Barrier blockedInSupplier = new Barrier();
237         final Barrier resumeInSupplier = new Barrier();
238 
239         final CompletableFuture<String> t1Result =
240                 runAndWaitForStart(() -> cache.get("alive", () -> {
241                     log.info("{}: About to await on barrierBulk", Thread.currentThread().getName());
242                     blockedInSupplier.signal();
243                     resumeInSupplier.await();
244                     log.info("{}: resuming", Thread.currentThread().getName());
245                     return "T1";
246                 }));
247 
248         blockedInSupplier.await();
249         final CompletableFuture<String> t2Result =
250                 runAndWaitForStart(() -> cache.get("alive", () -> "T2"));
251 
252         // Wait for the result from T2 to ensure it has run to completion.
253         assertThat(t2Result, successfulWith(is("T2")));
254 
255         log.info("{}: About to signal on barrierBulk", Thread.currentThread().getName());
256         resumeInSupplier.signal(); //Wait until T2 cache.get calls join
257         log.info("{}: and now", Thread.currentThread().getName());
258 
259         assertThat(t1Result, successfulWith(is("T2")));
260     }
261 
262     @Test
263     public void handle_legal_recursion_factory() {
264         final LocalCacheOperations<String, String> cache = createCache("abc");
265 
266         final Map<String, String> result = cache.getBulk(
267                 strings1 -> cache.getBulk(strings2 -> {
268                     return strings2.stream().collect(Collectors.toMap(k -> k, k -> k + "-2"));
269                 }, strings1),
270                 "first");
271 
272         assertThat(result, hasEntry("first", "first-2"));
273         assertThat(result.keySet(), contains("first"));
274     }
275 }