1 package net.sf.ehcache.terracotta;
2
3 import net.sf.ehcache.Cache;
4 import net.sf.ehcache.CacheManager;
5 import net.sf.ehcache.config.CacheConfiguration;
6 import net.sf.ehcache.store.TerracottaStore;
7 import org.junit.Test;
8 import org.mockito.invocation.InvocationOnMock;
9 import org.mockito.stubbing.Answer;
10
11 import java.io.File;
12 import java.io.IOException;
13 import java.lang.reflect.Field;
14 import java.text.ParseException;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.Set;
18 import java.util.concurrent.BrokenBarrierException;
19 import java.util.concurrent.CyclicBarrier;
20 import java.util.concurrent.atomic.AtomicLong;
21
22 import static org.hamcrest.CoreMatchers.is;
23 import static org.junit.Assert.assertThat;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.when;
26
27 /***
28 * @author Alex Snaps
29 */
30 public class KeySnapshotterTest {
31
32 private static final String KEY = "Key";
33 private static final long MAX_KEY = 10000;
34 private static final String DUMPS_DIRECTORY = System.getProperty("java.io.tmpdir") + File.separator + "dumps";
35
36 final CacheManager cacheManager = new CacheManager();
37
38 private static void deleteFolder(File root) {
39 if (root.isDirectory()) {
40 File[] files = root.listFiles();
41 for (File file : files) {
42 if (file.isDirectory()) {
43 deleteFolder(file);
44 } else {
45 file.delete();
46 }
47 }
48 }
49
50 if (root.exists()) {
51 root.delete();
52 }
53 }
54
55 @Test(expected = IllegalArgumentException.class)
56 public void testThrowsIllegalArgumentExceptionOnZeroInterval() {
57 new KeySnapshotter(createFakeTcClusteredCache(mock(TerracottaStore.class)), 0, false, null);
58 }
59
60 @Test(expected = IllegalArgumentException.class)
61 public void testThrowsIllegalArgumentExceptionOnNegativeInterval() {
62 new KeySnapshotter(createFakeTcClusteredCache(mock(TerracottaStore.class)), -100, false, null);
63 }
64
65 @Test(expected = NullPointerException.class)
66 public void testThrowsNullPointerExceptionOnNegativeInterval() {
67 new KeySnapshotter(createFakeTcClusteredCache(mock(TerracottaStore.class)), 10, false, null);
68 }
69
70 @Test
71 public void testOperatesOnDedicatedThread() throws ParseException, IOException {
72 final RotatingSnapshotFile rotatingSnapshotFile = mock(RotatingSnapshotFile.class);
73 final TerracottaStore mock = mock(TerracottaStore.class);
74 KeySnapshotter snapshotter = new KeySnapshotter(createFakeTcClusteredCache(mock), 10, true, rotatingSnapshotFile);
75
76 boolean found = false;
77 for (Thread thread : getAllThreads()) {
78 if (thread != null) {
79 if(thread.getName().equals("KeySnapshotter for cache test")) {
80 found = true;
81 }
82 }
83 }
84 assertThat(found, is(true));
85 snapshotter.dispose(true);
86 }
87
88 @Test
89 public void testDisposesProperlyImmediately() throws BrokenBarrierException, InterruptedException, IOException {
90 deleteFolder(new File(DUMPS_DIRECTORY));
91 final RotatingSnapshotFile rotatingSnapshotFile = new RotatingSnapshotFile(DUMPS_DIRECTORY, "testingInterruptImmediate");
92 final TerracottaStore mockedTcStore = mock(TerracottaStore.class);
93 KeySnapshotter snapshotter = new KeySnapshotter(createFakeTcClusteredCache(mockedTcStore), 1, true, rotatingSnapshotFile);
94 final CyclicBarrier barrier = new CyclicBarrier(2);
95 final Set mockedSet = mock(Set.class);
96 final AtomicLong counter = new AtomicLong(0);
97 when(mockedSet.iterator()).thenAnswer(new Answer<Iterator>() {
98 public Iterator answer(final InvocationOnMock invocationOnMock) throws Throwable {
99 return new Iterator<Object>() {
100
101 public boolean hasNext() {
102 return counter.get() < 100000;
103 }
104
105 public Object next() {
106 return "Key" + counter.getAndIncrement();
107 }
108
109 public void remove() {
110
111 }
112 };
113 }
114 });
115 when(mockedTcStore.getLocalKeys()).thenAnswer(new Answer<Set>() {
116 public Set answer(final InvocationOnMock invocationOnMock) throws Throwable {
117 barrier.await();
118 return mockedSet;
119 }
120 });
121 barrier.await();
122 snapshotter.dispose(true);
123 assertThat("We managed to get to a " + counter.get() + " keys written out", counter.get() < 1000, is(true));
124 final int elementsRead = rotatingSnapshotFile.readAll().size();
125 assertThat("Should be only a couple: " + elementsRead, elementsRead < 1000, is(true));
126 }
127
128 @Test
129 public void testDisposesProperlyButFinishes() throws BrokenBarrierException, InterruptedException, IOException {
130 deleteFolder(new File(DUMPS_DIRECTORY));
131 final RotatingSnapshotFile rotatingSnapshotFile = new RotatingSnapshotFile(DUMPS_DIRECTORY, "testingInterruptFinishes");
132 final TerracottaStore mockedTcStore = mock(TerracottaStore.class);
133 final CyclicBarrier barrier = new CyclicBarrier(2);
134 final Set mockedSet = mock(Set.class);
135 final AtomicLong counter = new AtomicLong(0);
136 when(mockedSet.iterator()).thenAnswer(new Answer<Iterator>() {
137 public Iterator answer(final InvocationOnMock invocationOnMock) throws Throwable {
138 return new Iterator<Object>() {
139
140 public boolean hasNext() {
141 return counter.get() < MAX_KEY;
142 }
143
144 public Object next() {
145 return KEY + counter.getAndIncrement();
146 }
147
148 public void remove() {
149
150 }
151 };
152 }
153 });
154 when(mockedTcStore.getLocalKeys()).thenAnswer(new Answer<Set>() {
155 public Set answer(final InvocationOnMock invocationOnMock) throws Throwable {
156 barrier.await();
157 return mockedSet;
158 }
159 });
160 KeySnapshotter snapshotter = new KeySnapshotter(createFakeTcClusteredCache(mockedTcStore), 1, true, rotatingSnapshotFile);
161 barrier.await();
162 snapshotter.dispose(false);
163 assertThat(counter.get(), is(MAX_KEY));
164 final Set<Object> objects = new HashSet<Object>(rotatingSnapshotFile.readAll());
165 assertThat(objects.size(), is((int) MAX_KEY));
166 for(int i = 0; i < MAX_KEY; i++) {
167 objects.remove(KEY + i);
168 }
169 assertThat(objects.isEmpty(), is(true));
170 }
171
172 private Thread[] getAllThreads() {
173 ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
174 ThreadGroup parentGroup;
175 while ((parentGroup = rootGroup.getParent()) != null) {
176 rootGroup = parentGroup;
177 }
178
179 Thread[] threads = new Thread[rootGroup.activeCount()];
180 rootGroup.enumerate(threads, true);
181 return threads;
182 }
183
184 private Cache createFakeTcClusteredCache(TerracottaStore store) {
185 final Cache cache = new Cache(new CacheConfiguration("test", 10));
186 cacheManager.addCache(cache);
187 try {
188 final Field compoundStore = cache.getClass().getDeclaredField("compoundStore");
189 compoundStore.setAccessible(true);
190 compoundStore.set(cache, store);
191 } catch (Exception e) {
192 throw new RuntimeException(e);
193 }
194 return cache;
195 }
196 }