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.containsString;
9 import static org.hamcrest.Matchers.instanceOf;
10 import static org.hamcrest.Matchers.is;
11 import static org.hamcrest.Matchers.nullValue;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Matchers.anyString;
14 import static org.mockito.Matchers.argThat;
15 import static org.mockito.Matchers.eq;
16 import static org.mockito.Mockito.doAnswer;
17 import static org.mockito.Mockito.mock;
18 import static org.mockito.Mockito.never;
19 import static org.mockito.Mockito.times;
20 import static org.mockito.Mockito.verify;
21 import static org.mockito.Mockito.when;
23 import java.io.IOException;
24 import java.util.HashMap;
27 import net.pterodactylus.sone.core.SoneInserter.ManifestCreator;
28 import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
29 import net.pterodactylus.sone.core.event.SoneEvent;
30 import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent;
31 import net.pterodactylus.sone.core.event.SoneInsertedEvent;
32 import net.pterodactylus.sone.core.event.SoneInsertingEvent;
33 import net.pterodactylus.sone.data.Album;
34 import net.pterodactylus.sone.data.Sone;
35 import net.pterodactylus.sone.main.SonePlugin;
37 import freenet.keys.FreenetURI;
38 import freenet.support.api.ManifestElement;
40 import com.google.common.base.Charsets;
41 import com.google.common.base.Optional;
42 import com.google.common.eventbus.AsyncEventBus;
43 import com.google.common.eventbus.EventBus;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.mockito.ArgumentCaptor;
47 import org.mockito.invocation.InvocationOnMock;
48 import org.mockito.stubbing.Answer;
51 * Unit test for {@link SoneInserter} and its subclasses.
53 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
55 public class SoneInserterTest {
57 private final Core core = mock(Core.class);
58 private final EventBus eventBus = mock(EventBus.class);
59 private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
62 public void setupCore() {
63 UpdateChecker updateChecker = mock(UpdateChecker.class);
64 when(core.getUpdateChecker()).thenReturn(updateChecker);
65 when(core.getSone(anyString())).thenReturn(Optional.<Sone>absent());
69 public void insertionDelayIsForwardedToSoneInserter() {
70 EventBus eventBus = new AsyncEventBus(sameThreadExecutor());
71 eventBus.register(new SoneInserter(core, eventBus, freenetInterface, "SoneId"));
72 eventBus.post(new InsertionDelayChangedEvent(15));
73 assertThat(SoneInserter.getInsertionDelay().get(), is(15));
76 private Sone createSone(FreenetURI insertUri, String fingerprint) {
77 Sone sone = mock(Sone.class);
78 when(sone.getInsertUri()).thenReturn(insertUri);
79 when(sone.getFingerprint()).thenReturn(fingerprint);
80 when(sone.getRootAlbum()).thenReturn(mock(Album.class));
81 when(core.getSone(anyString())).thenReturn(of(sone));
86 public void isModifiedIsTrueIfModificationDetectorSaysSo() {
87 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
88 when(soneModificationDetector.isModified()).thenReturn(true);
89 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
90 assertThat(soneInserter.isModified(), is(true));
94 public void isModifiedIsFalseIfModificationDetectorSaysSo() {
95 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
96 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
97 assertThat(soneInserter.isModified(), is(false));
101 public void lastFingerprintIsStoredCorrectly() {
102 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
103 soneInserter.setLastInsertFingerprint("last-fingerprint");
104 assertThat(soneInserter.getLastInsertFingerprint(), is("last-fingerprint"));
108 public void soneInserterStopsWhenItShould() {
109 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
111 soneInserter.serviceRun();
115 public void soneInserterInsertsASoneIfItIsEligible() throws SoneException {
116 FreenetURI insertUri = mock(FreenetURI.class);
117 final FreenetURI finalUri = mock(FreenetURI.class);
118 String fingerprint = "fingerprint";
119 Sone sone = createSone(insertUri, fingerprint);
120 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
121 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
122 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenReturn(finalUri);
123 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
124 doAnswer(new Answer<Void>() {
126 public Void answer(InvocationOnMock invocation) throws Throwable {
130 }).when(core).touchConfiguration();
131 soneInserter.serviceRun();
132 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
133 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
134 verify(eventBus, times(2)).post(soneEvents.capture());
135 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
136 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
137 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertedEvent.class));
138 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
142 public void soneInserterBailsOutIfItIsStoppedWhileInserting() throws SoneException {
143 FreenetURI insertUri = mock(FreenetURI.class);
144 final FreenetURI finalUri = mock(FreenetURI.class);
145 String fingerprint = "fingerprint";
146 Sone sone = createSone(insertUri, fingerprint);
147 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
148 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
149 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
150 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenAnswer(new Answer<FreenetURI>() {
152 public FreenetURI answer(InvocationOnMock invocation) throws Throwable {
157 soneInserter.serviceRun();
158 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
159 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
160 verify(eventBus, times(2)).post(soneEvents.capture());
161 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
162 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
163 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertedEvent.class));
164 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
165 verify(core, never()).touchConfiguration();
169 public void soneInserterDoesNotInsertSoneIfItIsNotEligible() throws SoneException {
170 FreenetURI insertUri = mock(FreenetURI.class);
171 String fingerprint = "fingerprint";
172 Sone sone = createSone(insertUri, fingerprint);
173 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
174 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
175 new Thread(new Runnable() {
180 } catch (InterruptedException ie1) {
181 throw new RuntimeException(ie1);
186 soneInserter.serviceRun();
187 verify(freenetInterface, never()).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
188 verify(eventBus, never()).post(argThat(org.hamcrest.Matchers.any(SoneEvent.class)));
192 public void soneInserterPostsAbortedEventIfAnExceptionOccurs() throws SoneException {
193 FreenetURI insertUri = mock(FreenetURI.class);
194 String fingerprint = "fingerprint";
195 Sone sone = createSone(insertUri, fingerprint);
196 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
197 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
198 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
199 final SoneException soneException = new SoneException(new Exception());
200 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenAnswer(new Answer<FreenetURI>() {
202 public FreenetURI answer(InvocationOnMock invocation) throws Throwable {
207 soneInserter.serviceRun();
208 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
209 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
210 verify(eventBus, times(2)).post(soneEvents.capture());
211 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
212 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
213 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertAbortedEvent.class));
214 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
215 verify(core, never()).touchConfiguration();
219 public void soneInserterExitsIfSoneIsUnknown() {
220 SoneModificationDetector soneModificationDetector =
221 mock(SoneModificationDetector.class);
222 SoneInserter soneInserter =
223 new SoneInserter(core, eventBus, freenetInterface, "SoneId",
224 soneModificationDetector, 1);
225 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
226 when(core.getSone("SoneId")).thenReturn(Optional.<Sone>absent());
227 soneInserter.serviceRun();
231 public void soneInserterCatchesExceptionAndContinues() {
232 SoneModificationDetector soneModificationDetector =
233 mock(SoneModificationDetector.class);
234 final SoneInserter soneInserter =
235 new SoneInserter(core, eventBus, freenetInterface, "SoneId",
236 soneModificationDetector, 1);
237 Answer<Optional<Sone>> stopInserterAndThrowException =
238 new Answer<Optional<Sone>>() {
240 public Optional<Sone> answer(
241 InvocationOnMock invocation) {
243 throw new NullPointerException();
246 when(soneModificationDetector.isEligibleForInsert()).thenAnswer(
247 stopInserterAndThrowException);
248 soneInserter.serviceRun();
252 public void templateIsRenderedCorrectlyForManifestElement()
254 Map<String, Object> soneProperties = new HashMap<String, Object>();
255 soneProperties.put("id", "SoneId");
256 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
257 long now = currentTimeMillis();
258 when(core.getStartupTime()).thenReturn(now);
259 ManifestElement manifestElement = manifestCreator.createManifestElement("test.txt", "plain/text; charset=utf-8", "sone-inserter-manifest.txt");
260 assertThat(manifestElement.getName(), is("test.txt"));
261 assertThat(manifestElement.getMimeTypeOverride(), is("plain/text; charset=utf-8"));
262 String templateContent = new String(toByteArray(manifestElement.getData().getInputStream()), Charsets.UTF_8);
263 assertThat(templateContent, containsString("Sone Version: " + SonePlugin.VERSION.toString() + "\n"));
264 assertThat(templateContent, containsString("Core Startup: " + now + "\n"));
265 assertThat(templateContent, containsString("Sone ID: " + "SoneId" + "\n"));
269 public void invalidTemplateReturnsANullManifestElement() {
270 Map<String, Object> soneProperties = new HashMap<String, Object>();
271 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
272 assertThat(manifestCreator.createManifestElement("test.txt",
273 "plain/text; charset=utf-8",
274 "sone-inserter-invalid-manifest.txt"),
279 public void errorWhileRenderingTemplateReturnsANullManifestElement() {
280 Map<String, Object> soneProperties = new HashMap<String, Object>();
281 ManifestCreator manifestCreator = new ManifestCreator(core, soneProperties);
282 when(core.toString()).thenThrow(NullPointerException.class);
283 assertThat(manifestCreator.createManifestElement("test.txt",
284 "plain/text; charset=utf-8",
285 "sone-inserter-faulty-manifest.txt"),