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;
24 import java.io.IOException;
25 import java.util.HashMap;
28 import net.pterodactylus.sone.core.SoneInserter.InsertInformation;
29 import net.pterodactylus.sone.core.SoneInserter.ManifestCreator;
30 import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
31 import net.pterodactylus.sone.core.event.SoneEvent;
32 import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent;
33 import net.pterodactylus.sone.core.event.SoneInsertedEvent;
34 import net.pterodactylus.sone.core.event.SoneInsertingEvent;
35 import net.pterodactylus.sone.data.Album;
36 import net.pterodactylus.sone.data.Sone;
37 import net.pterodactylus.sone.main.SonePlugin;
39 import freenet.client.async.ManifestElement;
40 import freenet.keys.FreenetURI;
42 import com.google.common.base.Charsets;
43 import com.google.common.base.Optional;
44 import com.google.common.eventbus.AsyncEventBus;
45 import com.google.common.eventbus.EventBus;
46 import org.junit.Before;
47 import org.junit.Test;
48 import org.mockito.ArgumentCaptor;
49 import org.mockito.invocation.InvocationOnMock;
50 import org.mockito.stubbing.Answer;
53 * Unit test for {@link SoneInserter} and its subclasses.
55 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
57 public class SoneInserterTest {
59 private final Core core = mock(Core.class);
60 private final EventBus eventBus = mock(EventBus.class);
61 private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
64 public void setupCore() {
65 UpdateChecker updateChecker = mock(UpdateChecker.class);
66 when(core.getUpdateChecker()).thenReturn(updateChecker);
67 when(core.getSone(anyString())).thenReturn(Optional.<Sone>absent());
71 public void insertionDelayIsForwardedToSoneInserter() {
72 EventBus eventBus = new AsyncEventBus(sameThreadExecutor());
73 eventBus.register(new SoneInserter(core, eventBus, freenetInterface, "SoneId"));
74 eventBus.post(new InsertionDelayChangedEvent(15));
75 assertThat(SoneInserter.getInsertionDelay().get(), is(15));
78 private Sone createSone(FreenetURI insertUri, String fingerprint) {
79 Sone sone = mock(Sone.class);
80 when(sone.getInsertUri()).thenReturn(insertUri);
81 when(sone.getFingerprint()).thenReturn(fingerprint);
82 when(sone.getRootAlbum()).thenReturn(mock(Album.class));
83 when(core.getSone(anyString())).thenReturn(of(sone));
88 public void isModifiedIsTrueIfModificationDetectorSaysSo() {
89 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
90 when(soneModificationDetector.isModified()).thenReturn(true);
91 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
92 assertThat(soneInserter.isModified(), is(true));
96 public void isModifiedIsFalseIfModificationDetectorSaysSo() {
97 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
98 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
99 assertThat(soneInserter.isModified(), is(false));
103 public void lastFingerprintIsStoredCorrectly() {
104 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
105 soneInserter.setLastInsertFingerprint("last-fingerprint");
106 assertThat(soneInserter.getLastInsertFingerprint(), is("last-fingerprint"));
110 public void soneInserterStopsWhenItShould() {
111 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
113 soneInserter.serviceRun();
117 public void soneInserterInsertsASoneIfItIsEligible() throws SoneException {
118 FreenetURI insertUri = mock(FreenetURI.class);
119 final FreenetURI finalUri = mock(FreenetURI.class);
120 String fingerprint = "fingerprint";
121 Sone sone = createSone(insertUri, fingerprint);
122 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
123 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
124 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenReturn(finalUri);
125 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
126 doAnswer(new Answer<Void>() {
128 public Void answer(InvocationOnMock invocation) throws Throwable {
132 }).when(core).touchConfiguration();
133 soneInserter.serviceRun();
134 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
135 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
136 verify(eventBus, times(2)).post(soneEvents.capture());
137 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
138 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
139 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertedEvent.class));
140 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
144 public void soneInserterBailsOutIfItIsStoppedWhileInserting() throws SoneException {
145 FreenetURI insertUri = mock(FreenetURI.class);
146 final FreenetURI finalUri = mock(FreenetURI.class);
147 String fingerprint = "fingerprint";
148 Sone sone = createSone(insertUri, fingerprint);
149 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
150 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
151 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
152 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenAnswer(new Answer<FreenetURI>() {
154 public FreenetURI answer(InvocationOnMock invocation) throws Throwable {
159 soneInserter.serviceRun();
160 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
161 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
162 verify(eventBus, times(2)).post(soneEvents.capture());
163 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
164 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
165 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertedEvent.class));
166 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
167 verify(core, never()).touchConfiguration();
171 public void soneInserterDoesNotInsertSoneIfItIsNotEligible() throws SoneException {
172 FreenetURI insertUri = mock(FreenetURI.class);
173 String fingerprint = "fingerprint";
174 Sone sone = createSone(insertUri, fingerprint);
175 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
176 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
177 new Thread(new Runnable() {
182 } catch (InterruptedException ie1) {
183 throw new RuntimeException(ie1);
188 soneInserter.serviceRun();
189 verify(freenetInterface, never()).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
190 verify(eventBus, never()).post(argThat(org.hamcrest.Matchers.any(SoneEvent.class)));
194 public void soneInserterPostsAbortedEventIfAnExceptionOccurs() throws SoneException {
195 FreenetURI insertUri = mock(FreenetURI.class);
196 String fingerprint = "fingerprint";
197 Sone sone = createSone(insertUri, fingerprint);
198 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
199 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
200 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
201 final SoneException soneException = new SoneException(new Exception());
202 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenAnswer(new Answer<FreenetURI>() {
204 public FreenetURI answer(InvocationOnMock invocation) throws Throwable {
209 soneInserter.serviceRun();
210 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
211 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
212 verify(eventBus, times(2)).post(soneEvents.capture());
213 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
214 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
215 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertAbortedEvent.class));
216 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
217 verify(core, never()).touchConfiguration();
221 public void soneInserterExitsIfSoneIsUnknown() {
222 SoneModificationDetector soneModificationDetector =
223 mock(SoneModificationDetector.class);
224 SoneInserter soneInserter =
225 new SoneInserter(core, eventBus, freenetInterface, "SoneId",
226 soneModificationDetector, 1);
227 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
228 when(core.getSone("SoneId")).thenReturn(Optional.<Sone>absent());
229 soneInserter.serviceRun();
233 public void soneInserterCatchesExceptionAndContinues() {
234 SoneModificationDetector soneModificationDetector =
235 mock(SoneModificationDetector.class);
236 final SoneInserter soneInserter =
237 new SoneInserter(core, eventBus, freenetInterface, "SoneId",
238 soneModificationDetector, 1);
239 Answer<Optional<Sone>> stopInserterAndThrowException =
240 new Answer<Optional<Sone>>() {
242 public Optional<Sone> answer(
243 InvocationOnMock invocation) {
245 throw new NullPointerException();
248 when(soneModificationDetector.isEligibleForInsert()).thenAnswer(
249 stopInserterAndThrowException);
250 soneInserter.serviceRun();
254 public void templateIsRenderedCorrectlyForManifestElement()
256 Map<String, Object> soneProperties = new HashMap<String, Object>();
257 soneProperties.put("id", "SoneId");
258 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
259 long now = currentTimeMillis();
260 when(core.getStartupTime()).thenReturn(now);
261 ManifestElement manifestElement = manifestCreator.createManifestElement("test.txt", "plain/text; charset=utf-8", "sone-inserter-manifest.txt");
262 assertThat(manifestElement.getName(), is("test.txt"));
263 assertThat(manifestElement.getMimeTypeOverride(), is("plain/text; charset=utf-8"));
264 String templateContent = new String(toByteArray(manifestElement.getData().getInputStream()), Charsets.UTF_8);
265 assertThat(templateContent, containsString("Sone Version: " + SonePlugin.VERSION.toString() + "\n"));
266 assertThat(templateContent, containsString("Core Startup: " + now + "\n"));
267 assertThat(templateContent, containsString("Sone ID: " + "SoneId" + "\n"));
271 public void invalidTemplateReturnsANullManifestElement() {
272 Map<String, Object> soneProperties = new HashMap<String, Object>();
273 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
274 assertThat(manifestCreator.createManifestElement("test.txt",
275 "plain/text; charset=utf-8",
276 "sone-inserter-invalid-manifest.txt"),
281 public void errorWhileRenderingTemplateReturnsANullManifestElement() {
282 Map<String, Object> soneProperties = new HashMap<String, Object>();
283 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
284 when(core.toString()).thenThrow(NullPointerException.class);
285 assertThat(manifestCreator.createManifestElement("test.txt",
286 "plain/text; charset=utf-8",
287 "sone-inserter-faulty-manifest.txt"),