1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.rendering.internal.transformation.linkchecker

File LinkCheckerTransformationTest.java

 

Code metrics

8
110
14
2
344
215
18
0.16
7.86
7
1.29

Classes

Class Line # Actions
LinkCheckerTransformationTest 57 108 0% 17 4
0.9689922396.9%
LinkCheckerTransformationTest.StateAnswer 223 2 0% 1 0
1.0100%
 

Contributing tests

This file is covered by 7 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.rendering.internal.transformation.linkchecker;
21   
22    import java.io.StringReader;
23    import java.util.Collections;
24    import java.util.HashMap;
25    import java.util.Map;
26   
27    import org.junit.*;
28    import org.mockito.invocation.InvocationOnMock;
29    import org.mockito.stubbing.Answer;
30    import org.xwiki.observation.ObservationManager;
31    import org.xwiki.rendering.block.XDOM;
32    import org.xwiki.rendering.listener.MetaData;
33    import org.xwiki.rendering.parser.Parser;
34    import org.xwiki.rendering.transformation.Transformation;
35    import org.xwiki.rendering.transformation.TransformationContext;
36    import org.xwiki.rendering.transformation.linkchecker.InvalidURLEvent;
37    import org.xwiki.rendering.transformation.linkchecker.LinkCheckerTransformationConfiguration;
38    import org.xwiki.rendering.transformation.linkchecker.LinkContextDataProvider;
39    import org.xwiki.rendering.transformation.linkchecker.LinkState;
40    import org.xwiki.rendering.transformation.linkchecker.LinkStateManager;
41    import org.xwiki.rendering.transformation.linkchecker.script.LinkCheckerScriptService;
42    import org.xwiki.script.service.ScriptService;
43    import org.xwiki.test.LogRule;
44    import org.xwiki.test.annotation.AllComponents;
45    import org.xwiki.test.mockito.MockitoComponentManagerRule;
46   
47    import static org.junit.Assert.*;
48    import static org.mockito.Mockito.*;
49   
50    /**
51    * Unit tests for {@link LinkCheckerTransformation}.
52    *
53    * @version $Id: b15b06c47bdab7ffb45fabb36672ab7c92a4cfde $
54    * @since 3.3M1
55    */
56    @AllComponents
 
57    public class LinkCheckerTransformationTest
58    {
59    @Rule
 
60  7 toggle public LogRule logRule = new LogRule() {{
61  7 record(LogLevel.ERROR);
62  7 recordLoggingForType(DefaultLinkCheckerThread.class);
63    }};
64   
65    @Rule
66    public MockitoComponentManagerRule componentManager = new MockitoComponentManagerRule();
67   
 
68  7 toggle @After
69    public void cleanUp() throws Exception
70    {
71    // Make sure we stop the Link Checker thread after each test (since it's started automatically when looking
72    // up the LinkCheckerTransformation component.
73  7 Transformation transformation = this.componentManager.getInstance(Transformation.class, "linkchecker");
74  7 ((LinkCheckerTransformation) transformation).stopLinkCheckerThread();
75    }
76   
 
77  1 toggle @Test
78    public void transform() throws Exception
79    {
80  1 String input = ""
81    + "whatever"
82    + "[[http://ok||class=\"myclass\"]]"
83    + "[[invalid]]"
84    + "[[unsupportedrotocol://invalid]]";
85   
86  1 HTTPChecker httpChecker = this.componentManager.registerMockComponent(HTTPChecker.class);
87  1 when(httpChecker.check("http://ok")).thenReturn(200);
88  1 when(httpChecker.check("invalid")).thenReturn(0);
89   
90  1 LinkStateManager linkStateManager = this.componentManager.getInstance(LinkStateManager.class);
91  1 transformAndWait(input, linkStateManager, 2);
92   
93    // Verify we can access the link states through the Script Service
94  1 LinkCheckerScriptService service = this.componentManager.getInstance(ScriptService.class, "linkchecker");
95  1 Map<String, Map<String, LinkState>> states = service.getLinkStates();
96   
97  1 LinkState state1 = states.get("http://ok").get("default");
98  1 assertEquals(200, state1.getResponseCode());
99  1 LinkState state2 = states.get("invalid").get("default");
100  1 assertEquals(0, state2.getResponseCode());
101    }
102   
103    /**
104    * Verify behavior when a link has already been checked and it's asked to be checked again before the timeout has
105    * expired (for performance reasons we only recheck links after a certain timeout).
106    */
 
107  1 toggle @Test
108    public void transformWhenExistingLinkState() throws Exception
109    {
110    // Note: it's important that the first link in the input be the link that we're adding manually to the list
111    // of states below since below we're waiting to get 2 states before stopping our test. If it were inverted
112    // then we would get 2 states before we have time to process the link for which the state already exists.
113  1 String input = ""
114    + "[[http://ok]]"
115    + "[[http://newok]]";
116   
117    // Set some state in the Link State Manager to verify that if an item that is on the queue is the same as one
118    // already process not long ago, it's not processed again.
119  1 LinkStateManager linkStateManager = this.componentManager.getInstance(LinkStateManager.class);
120  1 Map<String, LinkState> contentReferences = new HashMap<>();
121  1 long initialTime = System.currentTimeMillis();
122  1 contentReferences.put("default", new LinkState(200, initialTime));
123  1 linkStateManager.getLinkStates().put("http://ok", contentReferences);
124   
125  1 HTTPChecker httpChecker = this.componentManager.registerMockComponent(HTTPChecker.class);
126  1 verify(httpChecker, never()).check("http://ok");
127  1 when(httpChecker.check("http://newok")).thenReturn(200);
128   
129  1 transformAndWait(input, linkStateManager, 2);
130   
131    // Verify we can access the link states through the Script Service
132  1 LinkCheckerScriptService service = this.componentManager.getInstance(ScriptService.class, "linkchecker");
133  1 Map<String, Map<String, LinkState>> states = service.getLinkStates();
134   
135  1 LinkState state1 = states.get("http://ok").get("default");
136  1 assertEquals(200, state1.getResponseCode());
137  1 assertEquals(initialTime, state1.getLastCheckedTime());
138   
139  1 LinkState state2 = states.get("http://newok").get("default");
140  1 assertEquals(200, state2.getResponseCode());
141    }
142   
143    /**
144    * Verify behavior when a link has already been checked and it's asked to be checked again but after the timeout
145    * has expired.
146    */
 
147  1 toggle @Test
148    public void transformWhenExistingLinkStateButAfterTimeoutHasExpired() throws Exception
149    {
150    // Note: it's important that the first link in the input be the link that we're updating below since below
151    // we're waiting to get 2 states before stopping our test. If it were inverted then we would get 2 states
152    // before we have time to process the link for which the state already exists.
153  1 String input = ""
154    + "[[http://ok]]"
155    + "[[http://newok]]";
156   
157    // Set some state in the Link State Manager to verify that if an item that is on the queue is the same as one
158    // already process not long ago, it's not processed again.
159  1 LinkStateManager linkStateManager = this.componentManager.getInstance(LinkStateManager.class);
160  1 Map<String, LinkState> contentReferences = new HashMap<>();
161  1 long initialTime = System.currentTimeMillis();
162  1 contentReferences.put("default", new LinkState(404, initialTime));
163  1 linkStateManager.getLinkStates().put("http://ok", contentReferences);
164   
165  1 HTTPChecker httpChecker = this.componentManager.registerMockComponent(HTTPChecker.class);
166  1 when(httpChecker.check("http://newok")).thenReturn(200);
167  1 when(httpChecker.check("http://ok")).thenReturn(200);
168   
169    // Modify the default timeout so that we don't have to wait too long for the test...
170  1 LinkCheckerTransformationConfiguration configuration =
171    this.componentManager.getInstance(LinkCheckerTransformationConfiguration.class);
172  1 configuration.setCheckTimeout(0L);
173   
174  1 transformAndWait(input, linkStateManager, 2);
175   
176    // We've put a timeout of 0ms but to be on the safe side we wait 1ms (since otherwise it could be possible
177    // that the above executes in less than 1ms.
178  1 Thread.sleep(1L);
179   
180    // Verify we can access the link states through the Script Service
181  1 LinkCheckerScriptService service = this.componentManager.getInstance(ScriptService.class, "linkchecker");
182  1 Map<String, Map<String, LinkState>> states = service.getLinkStates();
183   
184  1 LinkState state = states.get("http://ok").get("default");
185  1 assertEquals(200, state.getResponseCode());
186    }
187   
 
188  1 toggle @Test
189    public void transformWithSourceMetaData() throws Exception
190    {
191  1 String input = "[[http://ok]]";
192  1 Parser parser = this.componentManager.getInstance(Parser.class, "xwiki/2.0");
193  1 XDOM xdom = parser.parse(new StringReader(input));
194   
195    // Add MetaData Block
196  1 MetaData metaData = new MetaData();
197  1 metaData.addMetaData(MetaData.SOURCE, "source");
198  1 XDOM newXDOM = new XDOM(xdom.getChildren(), metaData);
199   
200  1 HTTPChecker httpChecker = this.componentManager.registerMockComponent(HTTPChecker.class);
201  1 when(httpChecker.check("http://ok")).thenReturn(200);
202   
203  1 Transformation transformation = this.componentManager.getInstance(Transformation.class, "linkchecker");
204  1 transformation.transform(newXDOM, new TransformationContext());
205   
206  1 LinkStateManager linkStateManager = this.componentManager.getInstance(LinkStateManager.class);
207  1 wait(linkStateManager, 1);
208   
209  1 assertNotNull(linkStateManager.getLinkStates().get("http://ok").get("source"));
210    }
211   
212    /**
213    * Verify that when the Observation Manager is available we send an InvalidURLEvent event when there's an invalid
214    * URL.
215    */
 
216  1 toggle @Test
217    public void transformAndSendEvent() throws Exception
218    {
219  1 ObservationManager observationManager = this.componentManager.registerMockComponent(ObservationManager.class);
220  1 HTTPChecker httpChecker = this.componentManager.registerMockComponent(HTTPChecker.class);
221  1 when(httpChecker.check("http://doesntexist")).thenReturn(404);
222   
 
223    class StateAnswer implements Answer
224    {
225    public boolean hasListenerBeenCalled;
226   
 
227  1 toggle @Override
228    public Object answer(InvocationOnMock invocationOnMock) throws Throwable
229    {
230  1 this.hasListenerBeenCalled = true;
231  1 return null;
232    }
233    }
234   
235  1 StateAnswer stateAnswer = new StateAnswer();
236  1 doAnswer(stateAnswer).when(observationManager).notify(
237    eq(new InvalidURLEvent("http://doesntexist")), any(Map.class));
238   
239  1 LinkStateManager linkStateManager = this.componentManager.getInstance(LinkStateManager.class);
240  1 transformAndWait("[[http://doesntexist]]", linkStateManager, 1);
241   
242    // Wait till the event has been sent since parseAndWait only waits for the link state to be in cache but it
243    // doesn't wait for the event to be sent.
244  2 while (!stateAnswer.hasListenerBeenCalled) {
245  1 Thread.sleep(100L);
246    }
247    }
248   
249    /**
250    * Verify that if a LinkContextDataProvider is available it's used to store context data in the LinkStateManager.
251    */
 
252  1 toggle @Test
253    public void transformWithLinkContextDataProvider() throws Exception
254    {
255  1 String input = "[[http://ok]]";
256   
257  1 HTTPChecker httpChecker = this.componentManager.registerMockComponent(HTTPChecker.class);
258  1 when(httpChecker.check("http://ok")).thenReturn(200);
259   
260    // Register a LinkContextDataProvider component
261  1 LinkContextDataProvider linkContextDataProvider =
262    this.componentManager.registerMockComponent(LinkContextDataProvider.class);
263  1 when(linkContextDataProvider.getContextData("http://ok", "default")).thenReturn(
264    Collections.<String, Object>singletonMap("contextKey", "contextValue"));
265   
266  1 LinkStateManager linkStateManager = this.componentManager.getInstance(LinkStateManager.class);
267  1 transformAndWait(input, linkStateManager, 1);
268   
269    // Assert states
270  1 LinkState linkState = linkStateManager.getLinkStates().get("http://ok").get("default");
271  1 assertEquals("contextValue", linkState.getContextData().get("contextKey"));
272    }
273   
274    /**
275    * Verify the anti-flooding mechanism.
276    */
 
277  1 toggle @Test
278    public void transformWithAntiFloodKickingIn() throws Exception
279    {
280    // Replace the Link checker Thread with a mock so that it doesn't remove any link item from the queue
281    // Note that the LinkCheckerTransformation getInstance() below will automatically start the Link Checker
282    // Thread.
283  1 this.componentManager.registerMockComponent(LinkCheckerThread.class);
284   
285  1 LinkCheckerTransformation transformation =
286    this.componentManager.getInstance(Transformation.class, "linkchecker");
287   
288  1 StringBuilder input = new StringBuilder();
289  12 for (int i = 0; i < LinkCheckerTransformation.MAX_LINKS_IN_QUEUE + 1; i++) {
290  11 String url = "url" + i;
291  11 input.append("[[url:").append(url).append("]]");
292    }
293   
294    // Render a first page with MAX_LINKS_IN_QUEUE + 1 links in it so that they're all added to the Check Queue
295  1 Parser xwiki20Parser = this.componentManager.getInstance(Parser.class, "xwiki/2.0");
296  1 XDOM xdom = xwiki20Parser.parse(new StringReader(input.toString()));
297  1 transformation.transform(xdom, new TransformationContext());
298  1 assertEquals(LinkCheckerTransformation.MAX_LINKS_IN_QUEUE + 1, transformation.getLinkQueue().size());
299   
300    // Now render a second page and verify no new links are added to the queue since it's already full!
301  1 transformation.transform(xdom, new TransformationContext());
302  1 assertEquals(LinkCheckerTransformation.MAX_LINKS_IN_QUEUE + 1, transformation.getLinkQueue().size());
303    }
304   
 
305  5 toggle private void transformAndWait(String input, LinkStateManager linkStateManager, int numberOfItemsToWaitFor)
306    throws Exception
307    {
308  5 transform(input);
309   
310    // At this point the links have been put on the queue and we're waiting for the Link Checker Thread to
311    // process them
312  5 wait(linkStateManager, numberOfItemsToWaitFor);
313    }
314   
 
315  5 toggle private void transform(String input) throws Exception
316    {
317  5 transform(input, null);
318    }
319   
 
320  5 toggle private void transform(String input, String source) throws Exception
321    {
322  5 Transformation transformation = this.componentManager.getInstance(Transformation.class, "linkchecker");
323  5 Parser xwiki20Parser = this.componentManager.getInstance(Parser.class, "xwiki/2.0");
324  5 XDOM xdom = xwiki20Parser.parse(new StringReader(input));
325   
326  5 if (source != null) {
327  0 MetaData metaData = new MetaData();
328  0 metaData.addMetaData(MetaData.SOURCE, source);
329  0 xdom = new XDOM(xdom.getChildren(), metaData);
330    }
331   
332  5 transformation.transform(xdom, new TransformationContext());
333    }
334   
 
335  6 toggle private void wait(LinkStateManager linkStateManager, int numberOfItemsToWaitFor) throws Exception
336    {
337  6 long time = System.currentTimeMillis();
338  31 while (linkStateManager.getLinkStates().size() != numberOfItemsToWaitFor) {
339  25 Thread.sleep(100L);
340    // Protect against infinite loop
341  25 assertTrue("Killed thread since it took too much time", System.currentTimeMillis() - time < 10000L);
342    }
343    }
344    }