d7d9b48173becf1397fc66d62f63ce2826e4ec07
[Sone.git] / src / test / java / net / pterodactylus / sone / core / WebOfTrustUpdaterTest.java
1 package net.pterodactylus.sone.core;
2
3 import static java.lang.Thread.sleep;
4 import static java.util.concurrent.TimeUnit.SECONDS;
5 import static org.hamcrest.MatcherAssert.assertThat;
6 import static org.hamcrest.Matchers.containsString;
7 import static org.hamcrest.Matchers.is;
8 import static org.hamcrest.Matchers.not;
9 import static org.mockito.ArgumentMatchers.eq;
10 import static org.mockito.Mockito.*;
11
12 import java.util.concurrent.CountDownLatch;
13
14 import net.pterodactylus.sone.core.WebOfTrustUpdaterImpl.AddContextJob;
15 import net.pterodactylus.sone.core.WebOfTrustUpdaterImpl.RemoveContextJob;
16 import net.pterodactylus.sone.core.WebOfTrustUpdaterImpl.SetPropertyJob;
17 import net.pterodactylus.sone.core.WebOfTrustUpdaterImpl.WebOfTrustContextUpdateJob;
18 import net.pterodactylus.sone.core.WebOfTrustUpdaterImpl.WebOfTrustUpdateJob;
19 import net.pterodactylus.sone.freenet.plugin.PluginException;
20 import net.pterodactylus.sone.freenet.wot.Identity;
21 import net.pterodactylus.sone.freenet.wot.OwnIdentity;
22 import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector;
23
24 import org.junit.Test;
25 import org.mockito.invocation.InvocationOnMock;
26 import org.mockito.stubbing.Answer;
27
28 /**
29  * Unit test for {@link WebOfTrustUpdaterImpl} and its subclasses.
30  */
31 public class WebOfTrustUpdaterTest {
32
33         private static final String CONTEXT = "test-context";
34         private static final String PROPERTY_NAME = "test-property";
35         private final WebOfTrustConnector webOfTrustConnector = mock(WebOfTrustConnector.class);
36         private final WebOfTrustUpdaterImpl webOfTrustUpdater = new WebOfTrustUpdaterImpl(webOfTrustConnector);
37         private final OwnIdentity ownIdentity = when(mock(OwnIdentity.class).getId()).thenReturn("own-identity-id").getMock();
38         private final WebOfTrustUpdateJob successfulWebOfTrustUpdateJob = createWebOfTrustUpdateJob(true);
39         private final WebOfTrustUpdateJob failingWebOfTrustUpdateJob = createWebOfTrustUpdateJob(false);
40         private final WebOfTrustContextUpdateJob contextUpdateJob = webOfTrustUpdater.new WebOfTrustContextUpdateJob(ownIdentity, CONTEXT);
41         private final AddContextJob addContextJob = webOfTrustUpdater.new AddContextJob(ownIdentity, CONTEXT);
42         private final RemoveContextJob removeContextJob = webOfTrustUpdater.new RemoveContextJob(ownIdentity, CONTEXT);
43
44         private WebOfTrustUpdateJob createWebOfTrustUpdateJob(final boolean success) {
45                 return webOfTrustUpdater.new WebOfTrustUpdateJob() {
46                         @Override
47                         public void run() {
48                                 super.run();
49                                 try {
50                                         sleep(100);
51                                 } catch (InterruptedException ie1) {
52                                         throw new RuntimeException(ie1);
53                                 }
54                                 finish(success);
55                         }
56                 };
57         }
58
59         @Test
60         public void webOfTrustUpdateJobWaitsUntilFinishedHasBeenCalledAndReturnsSuccess() throws InterruptedException {
61                 new Thread(successfulWebOfTrustUpdateJob).start();
62                 assertThat(successfulWebOfTrustUpdateJob.waitForCompletion(), is(true));
63         }
64
65         @Test
66         public void webOfTrustUpdateJobWaitsUntilFinishedHasBeenCalledAndReturnsFailure() throws InterruptedException {
67                 new Thread(failingWebOfTrustUpdateJob).start();
68                 assertThat(failingWebOfTrustUpdateJob.waitForCompletion(), is(false));
69         }
70
71         @Test
72         public void webOfTrustContextUpdateJobsAreEqualIfTheirClassOwnIdentityAndContextAreEqual() {
73                 WebOfTrustContextUpdateJob secondContextUpdateJob = webOfTrustUpdater.new WebOfTrustContextUpdateJob(ownIdentity, CONTEXT);
74                 assertThat(contextUpdateJob.equals(secondContextUpdateJob), is(true));
75                 assertThat(secondContextUpdateJob.equals(contextUpdateJob), is(true));
76                 assertThat(contextUpdateJob.hashCode(), is(secondContextUpdateJob.hashCode()));
77         }
78
79         @Test
80         public void webOfTrustContextUpdatesJobsAreNotEqualIfTheirClassDiffers() {
81                 assertThat(contextUpdateJob.equals(addContextJob), is(false));
82         }
83
84         @Test
85         public void webOfTrustContextUpdateJobToStringContainsIdentityAndContext() {
86                 assertThat(contextUpdateJob.toString(), containsString(ownIdentity.toString()));
87                 assertThat(contextUpdateJob.toString(), containsString(CONTEXT));
88         }
89
90         @Test
91         public void webOfTrustContextUpdateJobsAreNotEqualIfTheIdentitiesDiffer() {
92                 OwnIdentity ownIdentity = mock(OwnIdentity.class);
93                 WebOfTrustContextUpdateJob secondContextUpdateJob = webOfTrustUpdater.new WebOfTrustContextUpdateJob(ownIdentity, CONTEXT);
94                 assertThat(contextUpdateJob.equals(secondContextUpdateJob), is(false));
95                 assertThat(secondContextUpdateJob.equals(contextUpdateJob), is(false));
96         }
97
98         @Test
99         public void webOfTrustContextUpdateJobsAreNotEqualIfTheirContextsDiffer() {
100                 WebOfTrustContextUpdateJob secondContextUpdateJob = webOfTrustUpdater.new WebOfTrustContextUpdateJob(ownIdentity, CONTEXT + CONTEXT);
101                 assertThat(contextUpdateJob.equals(secondContextUpdateJob), is(false));
102                 assertThat(secondContextUpdateJob.equals(contextUpdateJob), is(false));
103         }
104
105         @Test
106         public void webOfTrustContextUpdateJobsAreNotEqualToNull() {
107                 assertThat(contextUpdateJob.equals(null), is(false));
108         }
109
110         @Test
111         public void addContextJobAddsTheContext() throws PluginException {
112                 addContextJob.run();
113                 verify(webOfTrustConnector).addContext(eq(ownIdentity), eq(CONTEXT));
114                 verify(ownIdentity).addContext(eq(CONTEXT));
115                 assertThat(addContextJob.waitForCompletion(), is(true));
116         }
117
118         @Test
119         public void exceptionWhileAddingAContextIsExposed() throws PluginException {
120                 doThrow(PluginException.class).when(webOfTrustConnector).addContext(eq(ownIdentity), eq(CONTEXT));
121                 addContextJob.run();
122                 verify(webOfTrustConnector).addContext(eq(ownIdentity), eq(CONTEXT));
123                 verify(ownIdentity, never()).addContext(eq(CONTEXT));
124                 assertThat(addContextJob.waitForCompletion(), is(false));
125         }
126
127         @Test
128         public void removeContextJobRemovesTheContext() throws PluginException {
129                 removeContextJob.run();
130                 verify(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
131                 verify(ownIdentity).removeContext(eq(CONTEXT));
132                 assertThat(removeContextJob.waitForCompletion(), is(true));
133         }
134
135         @Test
136         public void exceptionWhileRemovingAContextIsExposed() throws PluginException {
137                 doThrow(PluginException.class).when(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
138                 removeContextJob.run();
139                 verify(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
140                 verify(ownIdentity, never()).removeContext(eq(CONTEXT));
141                 assertThat(removeContextJob.waitForCompletion(), is(false));
142         }
143
144         @Test
145         public void settingAPropertySetsTheProperty() throws PluginException {
146                 String propertyName = "property-name";
147                 String propertyValue = "property-value";
148                 SetPropertyJob setPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
149                 setPropertyJob.run();
150                 verify(webOfTrustConnector).setProperty(eq(ownIdentity), eq(propertyName), eq(propertyValue));
151                 verify(ownIdentity).setProperty(eq(propertyName), eq(propertyValue));
152                 assertThat(setPropertyJob.waitForCompletion(), is(true));
153         }
154
155         @Test
156         public void settingAPropertyToNullRemovesTheProperty() throws PluginException {
157                 String propertyName = "property-name";
158                 SetPropertyJob setPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, null);
159                 setPropertyJob.run();
160                 verify(webOfTrustConnector).removeProperty(eq(ownIdentity), eq(propertyName));
161                 verify(ownIdentity).removeProperty(eq(propertyName));
162                 assertThat(setPropertyJob.waitForCompletion(), is(true));
163         }
164
165         @Test
166         public void pluginExceptionWhileSettingAPropertyIsHandled() throws PluginException {
167                 String propertyName = "property-name";
168                 String propertyValue = "property-value";
169                 doThrow(PluginException.class).when(webOfTrustConnector).setProperty(eq(ownIdentity), eq(propertyName), eq(propertyValue));
170                 SetPropertyJob setPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
171                 setPropertyJob.run();
172                 verify(webOfTrustConnector).setProperty(eq(ownIdentity), eq(propertyName), eq(propertyValue));
173                 verify(ownIdentity, never()).setProperty(eq(propertyName), eq(propertyValue));
174                 assertThat(setPropertyJob.waitForCompletion(), is(false));
175         }
176
177         @Test
178         public void setPropertyJobsWithSameClassPropertyAndValueAreEqual() {
179                 String propertyName = "property-name";
180                 String propertyValue = "property-value";
181                 SetPropertyJob firstSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
182                 SetPropertyJob secondSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
183                 assertThat(firstSetPropertyJob, is(secondSetPropertyJob));
184                 assertThat(secondSetPropertyJob, is(firstSetPropertyJob));
185                 assertThat(firstSetPropertyJob.hashCode(), is(secondSetPropertyJob.hashCode()));
186         }
187
188         @Test
189         public void setPropertyJobsWithDifferentClassesAreNotEqual() {
190                 String propertyName = "property-name";
191                 String propertyValue = "property-value";
192                 SetPropertyJob firstSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
193                 SetPropertyJob secondSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue) {
194                 };
195                 assertThat(firstSetPropertyJob, not(is(secondSetPropertyJob)));
196         }
197
198         @Test
199         public void nullIsNotASetProjectJobEither() {
200                 String propertyName = "property-name";
201                 String propertyValue = "property-value";
202                 SetPropertyJob setPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
203                 assertThat(setPropertyJob, not(is((Object) null)));
204         }
205
206         @Test
207         public void setPropertyJobsWithDifferentPropertiesAreNotEqual() {
208                 String propertyName = "property-name";
209                 String propertyValue = "property-value";
210                 SetPropertyJob firstSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
211                 SetPropertyJob secondSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName + "2", propertyValue);
212                 assertThat(firstSetPropertyJob, not(is(secondSetPropertyJob)));
213         }
214
215         @Test
216         public void setPropertyJobsWithDifferentOwnIdentitiesAreNotEqual() {
217                 OwnIdentity otherOwnIdentity = mock(OwnIdentity.class);
218                 String propertyName = "property-name";
219                 String propertyValue = "property-value";
220                 SetPropertyJob firstSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
221                 SetPropertyJob secondSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(otherOwnIdentity, propertyName, propertyValue);
222                 assertThat(firstSetPropertyJob, not(is(secondSetPropertyJob)));
223         }
224
225         @Test
226         public void webOfTrustUpdaterStopsWhenItShould() {
227                 webOfTrustUpdater.stop();
228                 webOfTrustUpdater.serviceRun();
229         }
230
231         @Test
232         public void webOfTrustUpdaterStopsAfterItWasStarted() {
233                 webOfTrustUpdater.start();
234                 webOfTrustUpdater.stop();
235         }
236
237         @Test
238         public void removePropertyRemovesProperty() throws InterruptedException, PluginException {
239                 final CountDownLatch wotCallTriggered = new CountDownLatch(1);
240                 doAnswer(new Answer<Void>() {
241                         @Override
242                         public Void answer(InvocationOnMock invocation) throws Throwable {
243                                 wotCallTriggered.countDown();
244                                 return null;
245                         }
246                 }).when(webOfTrustConnector).removeProperty(eq(ownIdentity), eq(PROPERTY_NAME));
247                 webOfTrustUpdater.removeProperty(ownIdentity, PROPERTY_NAME);
248                 webOfTrustUpdater.start();
249                 assertThat(wotCallTriggered.await(1, SECONDS), is(true));
250         }
251
252         @Test
253         public void multipleCallsToSetPropertyAreCollapsed() throws InterruptedException, PluginException {
254                 final CountDownLatch wotCallTriggered = new CountDownLatch(1);
255                 doAnswer(new Answer<Void>() {
256                         @Override
257                         public Void answer(InvocationOnMock invocation) throws Throwable {
258                                 wotCallTriggered.countDown();
259                                 return null;
260                         }
261                 }).when(webOfTrustConnector).removeProperty(eq(ownIdentity), eq(PROPERTY_NAME));
262                 webOfTrustUpdater.removeProperty(ownIdentity, PROPERTY_NAME);
263                 webOfTrustUpdater.removeProperty(ownIdentity, PROPERTY_NAME);
264                 webOfTrustUpdater.start();
265                 assertThat(wotCallTriggered.await(1, SECONDS), is(true));
266                 verify(webOfTrustConnector).removeProperty(eq(ownIdentity), eq(PROPERTY_NAME));
267         }
268
269         @Test
270         public void addContextWaitWaitsForTheContextToBeAdded() {
271                 webOfTrustUpdater.start();
272                 assertThat(webOfTrustUpdater.addContextWait(ownIdentity, CONTEXT), is(true));
273                 verify(ownIdentity).addContext(eq(CONTEXT));
274         }
275
276         @Test
277         public void removeContextRemovesAContext() throws InterruptedException, PluginException {
278                 webOfTrustUpdater.start();
279                 final CountDownLatch removeContextTrigger = new CountDownLatch(1);
280                 doAnswer(new Answer<Void>() {
281                         @Override
282                         public Void answer(InvocationOnMock invocation) throws Throwable {
283                                 removeContextTrigger.countDown();
284                                 return null;
285                         }
286                 }).when(ownIdentity).removeContext(eq(CONTEXT));
287                 webOfTrustUpdater.removeContext(ownIdentity, CONTEXT);
288                 removeContextTrigger.await(1, SECONDS);
289                 verify(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
290                 verify(ownIdentity).removeContext(eq(CONTEXT));
291         }
292
293         @Test
294         public void removeContextRequestsAreCoalesced() throws InterruptedException, PluginException {
295                 final CountDownLatch contextRemovedTrigger = new CountDownLatch(1);
296                 doAnswer(new Answer<Void>() {
297                         @Override
298                         public Void answer(InvocationOnMock invocation) throws Throwable {
299                                 contextRemovedTrigger.countDown();
300                                 return null;
301                         }
302                 }).when(ownIdentity).removeContext(eq(CONTEXT));
303                 for (int i = 1; i <= 2; i++) {
304                         /* this is so fucking volatile. */
305                         if (i > 1) {
306                                 sleep(200);
307                         }
308                         new Thread(new Runnable() {
309                                 public void run() {
310                                         webOfTrustUpdater.removeContext(ownIdentity, CONTEXT);
311                                 }
312                         }).start();
313                 }
314                 webOfTrustUpdater.start();
315                 assertThat(contextRemovedTrigger.await(1, SECONDS), is(true));
316                 verify(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
317                 verify(ownIdentity).removeContext(eq(CONTEXT));
318         }
319
320 }