View Javadoc

1   package com.atlassian.vcache.internal.test;
2   
3   import com.atlassian.utt.concurrency.Barrier;
4   import com.atlassian.utt.concurrency.TestThread;
5   import com.atlassian.vcache.JvmCache;
6   import com.atlassian.vcache.JvmCacheSettings;
7   import com.atlassian.vcache.JvmCacheSettingsBuilder;
8   import com.atlassian.vcache.LocalCacheOperations;
9   import com.atlassian.vcache.VCache;
10  import org.junit.Test;
11  
12  import java.time.Duration;
13  import java.util.Arrays;
14  import java.util.concurrent.CountDownLatch;
15  import java.util.concurrent.TimeUnit;
16  import java.util.concurrent.atomic.AtomicInteger;
17  import java.util.function.Consumer;
18  import java.util.function.Supplier;
19  import java.util.stream.Collectors;
20  
21  import static org.hamcrest.Matchers.containsInAnyOrder;
22  import static org.hamcrest.Matchers.is;
23  import static org.hamcrest.Matchers.isOneOf;
24  import static org.junit.Assert.assertThat;
25  
26  /**
27   * Base test class for the {@link JvmCache}.
28   */
29  public abstract class AbstractJvmCacheTest extends AbstractLocalCacheOperationsTest {
30  
31      protected abstract <K, V> JvmCache<K, V> createCache(String name, JvmCacheSettings settings);
32  
33      @Override
34      protected final <K, V> JvmCache<K, V> createCache(String name) {
35          final JvmCacheSettings settings = new JvmCacheSettingsBuilder().maxEntries(3).defaultTtl(Duration.ofSeconds(10)).build();
36          return createCache(name, settings);
37      }
38  
39      @Test
40      public void testGetName() throws Exception {
41          final VCache namedCache = createCache("jvmcache");
42          assertThat(namedCache.getName(), is("jvmcache"));
43      }
44  
45      @SuppressWarnings("unchecked")
46      @Test
47      public void testKeys() {
48          final JvmCache<String, String> mycache = createCache("ignored");
49  
50          assertThat(mycache.getKeys(), containsInAnyOrder());
51  
52          mycache.put("k1", "v1");
53  
54          assertThat(mycache.getKeys(), containsInAnyOrder("k1"));
55  
56          mycache.put("k2", "v2");
57  
58          assertThat(mycache.getKeys(), containsInAnyOrder("k1", "k2"));
59      }
60  
61      @Test
62      public void testSize() {
63          final JvmCache<String, String> mycache = createCache("ignored");
64  
65          assertThat(mycache.getKeys().size(), is(0));
66  
67          mycache.put("k1", "v1");
68  
69          assertThat(mycache.getKeys().size(), is(1));
70  
71          mycache.put("k2", "v2");
72  
73          assertThat(mycache.getKeys().size(), is(2));
74  
75          mycache.put("k3", "v3");
76  
77          assertThat(mycache.getKeys().size(), is(3));
78  
79          mycache.put("k4", "v4");
80  
81          assertThat(mycache.getKeys().size(), is(3));
82  
83          Arrays.asList("k1", "k2", "k3", "k4").forEach(mycache::remove);
84  
85          assertThat(mycache.getKeys().size(), is(0));
86      }
87  
88      @Test
89      public void testRemoveConcurrentWithSupplier() {
90          removeConcurrentWithSupplier(c -> c.remove(0));
91      }
92  
93      @Test
94      public void testRemoveAllConcurrentWithSupplier() {
95          removeConcurrentWithSupplier(LocalCacheOperations::removeAll);
96      }
97  
98      private void removeConcurrentWithSupplier(Consumer<JvmCache<Integer, Integer>> removeFn) {
99          final Barrier doUpdate = new Barrier();
100         final CountDownLatch removeCalled = new CountDownLatch(1);
101         final Barrier afterGet = new Barrier();
102         final AtomicInteger dbValue = new AtomicInteger(1);
103 
104         final Supplier<Integer> loader = () -> {
105             final int value = dbValue.get();
106             doUpdate.trySignal();
107             try {
108                 // Whether or not remove gets blocked by the load operation is implementation-dependent.
109                 removeCalled.await(250L, TimeUnit.MILLISECONDS);
110             } catch (InterruptedException ie) {
111                 throw new AssertionError(ie);
112             }
113             afterGet.trySignal();
114             return value;
115         };
116 
117         final JvmCache<Integer, Integer> mycache = createCache("ignored");
118 
119         final TestThread blockingGet = new TestThread("blockingGet") {
120             @Override
121             protected void go() throws Exception {
122                 assertThat(mycache.get(0, loader), isOneOf(1, 2));  // Either the stale or updated value is acceptable
123                 removeCalled.await();
124                 assertThat(mycache.get(0, loader), is(2));
125             }
126         };
127 
128         final TestThread modifyRemoveGet = new TestThread("modifyRemoveGet") {
129             @Override
130             protected void go() throws Exception {
131                 doUpdate.await();
132                 dbValue.set(2);
133                 removeFn.accept(mycache);
134                 removeCalled.countDown();
135                 assertThat(mycache.get(0, loader), is(2));
136             }
137         };
138 
139         TestThread.runTest(blockingGet, modifyRemoveGet);
140     }
141 
142     @Test
143     public void detect_illegal_recursion_factory() {
144         thrown.expect(IllegalStateException.class);
145         thrown.expectMessage("Recursive call with key: first");
146 
147         final JvmCache<String, String> cache = createCache("abc");
148 
149         cache.getBulk(
150                 strings1 -> {
151                     return cache.getBulk(strings2 -> {
152                         return strings2.stream().collect(Collectors.toMap(k -> k, k -> k + "-2"));
153                     }, strings1);
154                 },
155                 "first");
156     }
157 }