1 package net.pterodactylus.sone.core;
3 import static com.google.common.base.Optional.of;
4 import static com.google.common.io.ByteStreams.toByteArray;
5 import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
6 import static java.lang.System.currentTimeMillis;
7 import static org.hamcrest.MatcherAssert.assertThat;
8 import static org.hamcrest.Matchers.containsInAnyOrder;
9 import static org.hamcrest.Matchers.containsString;
10 import static org.hamcrest.Matchers.instanceOf;
11 import static org.hamcrest.Matchers.is;
12 import static org.hamcrest.Matchers.nullValue;
13 import static org.mockito.Matchers.any;
14 import static org.mockito.Matchers.anyString;
15 import static org.mockito.Matchers.argThat;
16 import static org.mockito.Matchers.eq;
17 import static org.mockito.Mockito.doAnswer;
18 import static org.mockito.Mockito.mock;
19 import static org.mockito.Mockito.never;
20 import static org.mockito.Mockito.times;
21 import static org.mockito.Mockito.verify;
22 import static org.mockito.Mockito.when;
23 import static org.mockito.Mockito.withSettings;
25 import java.io.IOException;
26 import java.util.HashMap;
29 import net.pterodactylus.sone.core.SoneInserter.InsertInformation;
30 import net.pterodactylus.sone.core.SoneInserter.ManifestCreator;
31 import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
32 import net.pterodactylus.sone.core.event.SoneEvent;
33 import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent;
34 import net.pterodactylus.sone.core.event.SoneInsertedEvent;
35 import net.pterodactylus.sone.core.event.SoneInsertingEvent;
36 import net.pterodactylus.sone.data.Album;
37 import net.pterodactylus.sone.data.LocalSone;
38 import net.pterodactylus.sone.data.Sone;
39 import net.pterodactylus.sone.main.SonePlugin;
41 import freenet.client.async.ManifestElement;
42 import freenet.keys.FreenetURI;
44 import com.google.common.base.Charsets;
45 import com.google.common.base.Optional;
46 import com.google.common.eventbus.AsyncEventBus;
47 import com.google.common.eventbus.EventBus;
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.mockito.ArgumentCaptor;
51 import org.mockito.invocation.InvocationOnMock;
52 import org.mockito.stubbing.Answer;
55 * Unit test for {@link SoneInserter} and its subclasses.
57 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
59 public class SoneInserterTest {
61 private final Core core = mock(Core.class);
62 private final EventBus eventBus = mock(EventBus.class);
63 private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
66 public void setupCore() {
67 UpdateChecker updateChecker = mock(UpdateChecker.class);
68 when(core.getUpdateChecker()).thenReturn(updateChecker);
69 when(core.getSone(anyString())).thenReturn(Optional.<Sone>absent());
70 when(core.getLocalSone(anyString())).thenReturn(Optional.<LocalSone>absent());
74 public void insertionDelayIsForwardedToSoneInserter() {
75 EventBus eventBus = new AsyncEventBus(sameThreadExecutor());
76 eventBus.register(new SoneInserter(core, eventBus, freenetInterface, "SoneId"));
77 eventBus.post(new InsertionDelayChangedEvent(15));
78 assertThat(SoneInserter.getInsertionDelay().get(), is(15));
81 private Sone createSone(FreenetURI insertUri, String fingerprint) {
82 Sone sone = mock(Sone.class, withSettings().extraInterfaces(LocalSone.class));
83 when(sone.getInsertUri()).thenReturn(insertUri);
84 when(sone.getFingerprint()).thenReturn(fingerprint);
85 when(sone.getRootAlbum()).thenReturn(mock(Album.class));
86 when(core.getSone(anyString())).thenReturn(of(sone));
87 when(core.getLocalSone(anyString())).thenReturn(of((LocalSone) sone));
92 public void isModifiedIsTrueIfModificationDetectorSaysSo() {
93 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
94 when(soneModificationDetector.isModified()).thenReturn(true);
95 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
96 assertThat(soneInserter.isModified(), is(true));
100 public void isModifiedIsFalseIfModificationDetectorSaysSo() {
101 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
102 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
103 assertThat(soneInserter.isModified(), is(false));
107 public void lastFingerprintIsStoredCorrectly() {
108 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
109 soneInserter.setLastInsertFingerprint("last-fingerprint");
110 assertThat(soneInserter.getLastInsertFingerprint(), is("last-fingerprint"));
114 public void soneInserterStopsWhenItShould() {
115 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
117 soneInserter.serviceRun();
121 public void soneInserterInsertsASoneIfItIsEligible() throws SoneException {
122 FreenetURI insertUri = mock(FreenetURI.class);
123 final FreenetURI finalUri = mock(FreenetURI.class);
124 String fingerprint = "fingerprint";
125 Sone sone = createSone(insertUri, fingerprint);
126 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
127 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
128 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenReturn(finalUri);
129 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
130 doAnswer(new Answer<Void>() {
132 public Void answer(InvocationOnMock invocation) throws Throwable {
136 }).when(core).touchConfiguration();
137 soneInserter.serviceRun();
138 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
139 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
140 verify(eventBus, times(2)).post(soneEvents.capture());
141 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
142 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
143 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertedEvent.class));
144 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
148 public void soneInserterBailsOutIfItIsStoppedWhileInserting() throws SoneException {
149 FreenetURI insertUri = mock(FreenetURI.class);
150 final FreenetURI finalUri = mock(FreenetURI.class);
151 String fingerprint = "fingerprint";
152 Sone sone = createSone(insertUri, fingerprint);
153 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
154 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
155 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
156 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenAnswer(new Answer<FreenetURI>() {
158 public FreenetURI answer(InvocationOnMock invocation) throws Throwable {
163 soneInserter.serviceRun();
164 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
165 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
166 verify(eventBus, times(2)).post(soneEvents.capture());
167 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
168 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
169 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertedEvent.class));
170 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
171 verify(core, never()).touchConfiguration();
175 public void soneInserterDoesNotInsertSoneIfItIsNotEligible() throws SoneException {
176 FreenetURI insertUri = mock(FreenetURI.class);
177 String fingerprint = "fingerprint";
178 Sone sone = createSone(insertUri, fingerprint);
179 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
180 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
181 new Thread(new Runnable() {
186 } catch (InterruptedException ie1) {
187 throw new RuntimeException(ie1);
192 soneInserter.serviceRun();
193 verify(freenetInterface, never()).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
194 verify(eventBus, never()).post(argThat(org.hamcrest.Matchers.any(SoneEvent.class)));
198 public void soneInserterPostsAbortedEventIfAnExceptionOccurs() throws SoneException {
199 FreenetURI insertUri = mock(FreenetURI.class);
200 String fingerprint = "fingerprint";
201 Sone sone = createSone(insertUri, fingerprint);
202 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
203 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
204 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
205 final SoneException soneException = new SoneException(new Exception());
206 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenAnswer(new Answer<FreenetURI>() {
208 public FreenetURI answer(InvocationOnMock invocation) throws Throwable {
213 soneInserter.serviceRun();
214 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
215 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
216 verify(eventBus, times(2)).post(soneEvents.capture());
217 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
218 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
219 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertAbortedEvent.class));
220 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
221 verify(core, never()).touchConfiguration();
225 public void soneInserterExitsIfSoneIsUnknown() {
226 SoneModificationDetector soneModificationDetector =
227 mock(SoneModificationDetector.class);
228 SoneInserter soneInserter =
229 new SoneInserter(core, eventBus, freenetInterface, "SoneId",
230 soneModificationDetector, 1);
231 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
232 when(core.getSone("SoneId")).thenReturn(Optional.<Sone>absent());
233 soneInserter.serviceRun();
237 public void soneInserterCatchesExceptionAndContinues() {
238 SoneModificationDetector soneModificationDetector =
239 mock(SoneModificationDetector.class);
240 final SoneInserter soneInserter =
241 new SoneInserter(core, eventBus, freenetInterface, "SoneId",
242 soneModificationDetector, 1);
243 Answer<Optional<Sone>> stopInserterAndThrowException =
244 new Answer<Optional<Sone>>() {
246 public Optional<Sone> answer(
247 InvocationOnMock invocation) {
249 throw new NullPointerException();
252 when(soneModificationDetector.isEligibleForInsert()).thenAnswer(
253 stopInserterAndThrowException);
254 soneInserter.serviceRun();
258 public void templateIsRenderedCorrectlyForManifestElement()
260 Map<String, Object> soneProperties = new HashMap<String, Object>();
261 soneProperties.put("id", "SoneId");
262 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
263 long now = currentTimeMillis();
264 when(core.getStartupTime()).thenReturn(now);
265 ManifestElement manifestElement = manifestCreator.createManifestElement("test.txt", "plain/text; charset=utf-8", "sone-inserter-manifest.txt");
266 assertThat(manifestElement.getName(), is("test.txt"));
267 assertThat(manifestElement.getMimeTypeOverride(), is("plain/text; charset=utf-8"));
268 String templateContent = new String(toByteArray(manifestElement.getData().getInputStream()), Charsets.UTF_8);
269 assertThat(templateContent, containsString("Sone Version: " + SonePlugin.VERSION.toString() + "\n"));
270 assertThat(templateContent, containsString("Core Startup: " + now + "\n"));
271 assertThat(templateContent, containsString("Sone ID: " + "SoneId" + "\n"));
275 public void invalidTemplateReturnsANullManifestElement() {
276 Map<String, Object> soneProperties = new HashMap<String, Object>();
277 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
278 assertThat(manifestCreator.createManifestElement("test.txt",
279 "plain/text; charset=utf-8",
280 "sone-inserter-invalid-manifest.txt"),
285 public void errorWhileRenderingTemplateReturnsANullManifestElement() {
286 Map<String, Object> soneProperties = new HashMap<String, Object>();
287 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
288 when(core.toString()).thenThrow(NullPointerException.class);
289 assertThat(manifestCreator.createManifestElement("test.txt",
290 "plain/text; charset=utf-8",
291 "sone-inserter-faulty-manifest.txt"),