1 package net.sf.ehcache.terracotta;
2
3 import org.hamcrest.CoreMatchers;
4 import org.junit.After;
5 import org.junit.Before;
6 import org.junit.Test;
7
8 import java.io.File;
9 import java.io.IOException;
10 import java.util.HashSet;
11 import java.util.Iterator;
12 import java.util.Set;
13 import java.util.UUID;
14 import java.util.concurrent.atomic.AtomicReference;
15
16 import static org.hamcrest.CoreMatchers.equalTo;
17 import static org.hamcrest.CoreMatchers.is;
18 import static org.junit.Assert.assertThat;
19
20 /***
21 * @author Alex Snaps
22 */
23 public class RotatingSnapshotFileTest {
24
25 private static final String DIRECTORY = "./dumps";
26 private RotatingSnapshotFile snapshotFile;
27 private File cleanMeUp;
28
29 @Before
30 public void setup() {
31 snapshotFile = new RotatingSnapshotFile(DIRECTORY, "test");
32 }
33
34 @Test
35 public void testSuccessfulWrite() throws IOException {
36
37 Set<String> keys = populateWithValues(snapshotFile, 10000);
38 assertThat(snapshotFile.currentSnapshotFile().exists(), is(true));
39 assertThat(snapshotFile.tempSnapshotFile().exists(), is(false));
40 assertThat(snapshotFile.newSnapshotFile().exists(), is(false));
41 assertThat(snapshotFile.currentSnapshotFile().length() > 0, is(true));
42
43 final Set<String> snapshot = snapshotFile.readAll();
44 assertThat(snapshot.size(), equalTo(keys.size()));
45 for (String key : snapshot) {
46 keys.remove(key);
47 }
48 assertThat(keys.isEmpty(), is(true));
49 }
50
51 @Test
52 public void testStopsOnThreadInterrupted() throws IOException, InterruptedException {
53
54 final RotatingSnapshotFile file = new RotatingSnapshotFile(DIRECTORY, "killMe");
55 final AtomicReference<Throwable> throwable = new AtomicReference<Throwable>();
56 Set<String> keys = populateWithValues(file, 10000);
57 assertThat(file.currentSnapshotFile().exists(), is(true));
58 assertThat(file.tempSnapshotFile().exists(), is(false));
59 assertThat(file.newSnapshotFile().exists(), is(false));
60
61 Thread writerThread = new Thread() {
62
63 int valuesAskedWhileInterrupted = 0;
64
65 @Override
66 public void run() {
67 try {
68 file.writeAll(new Iterable<Object>() {
69 public Iterator<Object> iterator() {
70 return new Iterator<Object>() {
71 public boolean hasNext() {
72 if (isInterrupted() && ++valuesAskedWhileInterrupted > 1) {
73 throwable.set(new AssertionError("We shouldn't be asked for more values by now!"));
74 return false;
75 }
76 return true;
77 }
78
79 public Object next() {
80 return UUID.randomUUID();
81 }
82
83 public void remove() {
84
85 }
86 };
87 }
88 });
89 } catch (Throwable e) {
90 throwable.set(e);
91 }
92 }
93 };
94
95 writerThread.start();
96 Thread.sleep(500);
97 assertThat(file.currentSnapshotFile().exists(), is(true));
98 assertThat(file.newSnapshotFile().exists(), is(true));
99 assertThat(file.tempSnapshotFile().exists(), is(false));
100 file.setShutdownOnThreadInterrupted(true);
101 writerThread.interrupt();
102 writerThread.join();
103 assertThat(throwable.get(), CoreMatchers.<Object>nullValue());
104 assertThat(file.currentSnapshotFile().exists(), is(true));
105 assertThat(file.newSnapshotFile().exists(), is(true));
106 assertThat(file.tempSnapshotFile().exists(), is(false));
107 final Set<Object> values = file.readAll();
108 assertThat(values.size(), equalTo(keys.size()));
109 for (Object key : values) {
110 keys.remove(key);
111 }
112 assertThat(keys.isEmpty(), is(true));
113 assertThat(file.currentSnapshotFile().exists(), is(true));
114 assertThat(file.newSnapshotFile().exists(), is(false));
115 assertThat(file.tempSnapshotFile().exists(), is(false));
116
117 cleanMeUp = file.currentSnapshotFile();
118 }
119
120 @Test
121 public void testFinishesOnThreadInterrupted() throws IOException, InterruptedException {
122
123 final RotatingSnapshotFile file = new RotatingSnapshotFile(DIRECTORY, "killMe");
124 final AtomicReference<Throwable> throwable = new AtomicReference<Throwable>();
125 Set<String> keys = populateWithValues(file, 10000);
126 assertThat(file.currentSnapshotFile().exists(), is(true));
127 assertThat(file.tempSnapshotFile().exists(), is(false));
128 assertThat(file.newSnapshotFile().exists(), is(false));
129
130 final int keyAmount = 100;
131 final String keyPrefix = "Finish with value ";
132 Thread writerThread = new Thread() {
133
134 int valuesToDispenseOnceInterrupted = keyAmount;
135
136 @Override
137 public void run() {
138 try {
139 file.writeAll(new Iterable<Object>() {
140 public Iterator<Object> iterator() {
141 return new Iterator<Object>() {
142 public boolean hasNext() {
143 return !isInterrupted() || valuesToDispenseOnceInterrupted-- > 0;
144 }
145
146 public Object next() {
147 if (isInterrupted()) {
148 return keyPrefix + valuesToDispenseOnceInterrupted;
149 }
150 try {
151 Thread.sleep(200);
152 } catch (InterruptedException e) {
153 interrupt();
154 }
155 return UUID.randomUUID();
156 }
157
158 public void remove() {
159
160 }
161 };
162 }
163 });
164 } catch (Throwable e) {
165 throwable.set(e);
166 }
167 }
168 };
169
170 writerThread.start();
171 Thread.sleep(500);
172 assertThat(file.currentSnapshotFile().exists(), is(true));
173 assertThat(file.newSnapshotFile().exists(), is(true));
174 assertThat(file.tempSnapshotFile().exists(), is(false));
175 writerThread.interrupt();
176 writerThread.join();
177 assertThat(throwable.get(), CoreMatchers.<Object>nullValue());
178 assertThat(file.currentSnapshotFile().exists(), is(true));
179 assertThat(file.newSnapshotFile().exists(), is(false));
180 assertThat(file.tempSnapshotFile().exists(), is(false));
181 final Set<Object> values = file.readAll();
182 for (Object key : keys) {
183 assertThat(values.contains(key), is(false));
184 }
185 for (int i = 0; i < keyAmount; i++) {
186 final String key = keyPrefix + i;
187 assertThat(key + " is missing!", values.contains(key), is(true));
188 }
189 assertThat(values.contains(keyPrefix + keyAmount), is(false));
190 assertThat(values.size() > keyAmount, is(true));
191 assertThat(file.currentSnapshotFile().exists(), is(true));
192 assertThat(file.newSnapshotFile().exists(), is(false));
193 assertThat(file.tempSnapshotFile().exists(), is(false));
194
195 cleanMeUp = file.currentSnapshotFile();
196 }
197
198 private Set<String> populateWithValues(RotatingSnapshotFile snapshotFile, int amount) throws IOException {
199 Set<String> keys = new HashSet<String>();
200 for (int i = 0; i < amount; i++) {
201 keys.add("SomeKey that contains something " + i);
202 }
203
204 snapshotFile.writeAll(keys);
205 return keys;
206 }
207
208 @After
209 public void cleanUp() {
210 if (cleanMeUp != null) {
211 deleteSilently(cleanMeUp);
212 }
213
214 if (snapshotFile != null) {
215 deleteSilently(snapshotFile.currentSnapshotFile());
216 deleteSilently(snapshotFile.newSnapshotFile());
217 deleteSilently(snapshotFile.tempSnapshotFile());
218 deleteSilently(new File(DIRECTORY));
219 }
220 }
221
222 private void deleteSilently(final File file) {
223 try {
224 if (file.exists()) {
225 file.delete();
226 }
227 } catch (Throwable e) {
228 System.err.println("Error deleting file " + file.getAbsolutePath() + ": " + e.getMessage());
229 }
230 }
231
232 }