Clover icon

Magnolia Imaging Module 3.4.2-SUPPORT-10161

  1. Project Clover database Tue Jul 16 2019 23:33:19 EEST
  2. Package info.magnolia.imaging.caching

File CachingImageStreamerRepositoryTest.java

 

Code metrics

16
215
24
5
639
406
34
0.16
8.96
4.8
1.42
1.5% of code in this file is excluded from these metrics.

Classes

Class Line # Actions
CachingImageStreamerRepositoryTest 114 186 0.9% 22 72
0.661971866.2%
CachingImageStreamerRepositoryTest.TestJob 457 7 0% 2 0
1.0100%
CachingImageStreamerRepositoryTest.DelegatingSessionStrategy 567 11 0% 4 0
1.0100%
CachingImageStreamerRepositoryTest.TestParameterProviderFactory 596 6 20% 3 1
0.87587.5%
CachingImageStreamerRepositoryTest.SingleSaveSessionWrapper 622 5 0% 3 2
0.777777877.8%
 

Contributing tests

This file is covered by 3 tests. .

Source view

1    /**
2    * This file Copyright (c) 2009-2018 Magnolia International
3    * Ltd. (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10    * This file is distributed in the hope that it will be
11    * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12    * implied warranty of MERCHANTABILITY or FITNESS FOR A
13    * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14    * Redistribution, except as permitted by whichever of the GPL
15    * or MNA you select, is prohibited.
16    *
17    * 1. For the GPL license (GPL), you can redistribute and/or
18    * modify this file under the terms of the GNU General
19    * Public License, Version 3, as published by the Free Software
20    * Foundation. You should have received a copy of the GNU
21    * General Public License, Version 3 along with this program;
22    * if not, write to the Free Software Foundation, Inc., 51
23    * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24    *
25    * 2. For the Magnolia Network Agreement (MNA), this file
26    * and the accompanying materials are made available under the
27    * terms of the MNA which accompanies this distribution, and
28    * is available at http://www.magnolia-cms.com/mna.html
29    *
30    * Any modifications to this file must keep this entire header
31    * intact.
32    *
33    */
34    package info.magnolia.imaging.caching;
35   
36    import static org.hamcrest.Matchers.*;
37    import static org.junit.Assert.*;
38    import static org.mockito.Matchers.isA;
39    import static org.mockito.Mockito.*;
40   
41    import info.magnolia.cms.beans.config.MIMEMapping;
42    import info.magnolia.context.JCRSessionStrategy;
43    import info.magnolia.context.MgnlContext;
44    import info.magnolia.context.SystemContext;
45    import info.magnolia.imaging.AbstractRepositoryTestCase;
46    import info.magnolia.imaging.DefaultImageStreamer;
47    import info.magnolia.imaging.DummyImageResponse;
48    import info.magnolia.imaging.ImageGenerator;
49    import info.magnolia.imaging.ImageStreamer;
50    import info.magnolia.imaging.ImagingException;
51    import info.magnolia.imaging.OutputFormat;
52    import info.magnolia.imaging.ParameterProvider;
53    import info.magnolia.imaging.ParameterProviderFactory;
54    import info.magnolia.imaging.StringParameterProvider;
55    import info.magnolia.imaging.operations.ImageOperationChain;
56    import info.magnolia.imaging.operations.load.URLImageLoader;
57    import info.magnolia.imaging.parameters.BinaryNodeParameterProvider;
58    import info.magnolia.imaging.parameters.SimpleEqualityNodeWrapper;
59    import info.magnolia.jcr.predicate.NodeTypePredicate;
60    import info.magnolia.jcr.util.NodeTypes;
61    import info.magnolia.jcr.util.NodeUtil;
62    import info.magnolia.jcr.wrapper.DelegateSessionWrapper;
63    import info.magnolia.module.ModuleManagementException;
64    import info.magnolia.module.ModuleManager;
65    import info.magnolia.module.ModuleManagerImpl;
66    import info.magnolia.module.ModuleRegistry;
67    import info.magnolia.module.model.ModuleDefinition;
68    import info.magnolia.module.model.reader.ModuleDefinitionReader;
69    import info.magnolia.objectfactory.Components;
70    import info.magnolia.repository.RepositoryManager;
71    import info.magnolia.test.ComponentsTestUtil;
72    import info.magnolia.test.mock.MockContext;
73    import info.magnolia.test.mock.MockRepositoryManager;
74   
75    import java.awt.GraphicsEnvironment;
76    import java.awt.image.BufferedImage;
77    import java.io.ByteArrayOutputStream;
78    import java.io.IOException;
79    import java.io.InputStream;
80    import java.io.OutputStream;
81    import java.io.Reader;
82    import java.util.ArrayList;
83    import java.util.Arrays;
84    import java.util.HashMap;
85    import java.util.List;
86    import java.util.Map;
87    import java.util.concurrent.Callable;
88    import java.util.concurrent.ExecutorService;
89    import java.util.concurrent.Executors;
90    import java.util.concurrent.Future;
91    import java.util.concurrent.TimeUnit;
92   
93    import javax.imageio.ImageIO;
94    import javax.jcr.Node;
95    import javax.jcr.RepositoryException;
96    import javax.jcr.Session;
97    import javax.servlet.http.HttpServletRequest;
98   
99    import org.apache.commons.io.IOUtils;
100    import org.apache.jackrabbit.JcrConstants;
101    import org.apache.jackrabbit.commons.predicate.Predicates;
102    import org.junit.Before;
103    import org.junit.Ignore;
104    import org.junit.Test;
105   
106    import com.google.common.net.MediaType;
107   
108    /**
109    * Tests for {@link info.magnolia.imaging.caching.CachingImageStreamer}.
110    *
111    * TODO : this is sort of a load test and uses a real (in memory) repository.
112    * TODO : cleanup.
113    */
 
114    public class CachingImageStreamerRepositoryTest extends AbstractRepositoryTestCase {
115    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CachingImageStreamerRepositoryTest.class);
116    private Session websiteSession;
117    private Session configSession;
118    private String nodePath;
119   
 
120  3 toggle @Override
121    @Before
122    public void setUp() throws Exception {
123    // this used to set autostart to false, but I'm not sure why.
124    // It seems fine as is.
125   
126  3 super.setUp();
127   
128    // Now replace JcrSessionStrategy instances (current ctx and system ctx) with a wrapper than ensures we only save once, for the purpose of these tests.
129  3 final MockContext systemContext = (MockContext) MgnlContext.getSystemContext();
130  3 systemContext.setRepositoryStrategy(new DelegatingSessionStrategy(systemContext.getRepositoryStrategy()));
131   
132  3 final MockContext regularContext = (MockContext) MgnlContext.getInstance();
133  3 regularContext.setRepositoryStrategy(new DelegatingSessionStrategy(regularContext.getRepositoryStrategy()));
134   
135  3 final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
136  3 if (!ge.isHeadless()) {
137  0 log.warn("This test should run in headless mode as the server will likely run headless too!!!!!");
138    }
139   
140  3 websiteSession = MgnlContext.getJCRSession("website");
141  3 nodePath = "/foo/bar";
142  3 NodeUtil.createPath(websiteSession.getRootNode(), nodePath, NodeTypes.ContentNode.NAME);
143   
144  3 configSession = MgnlContext.getJCRSession("config");
145  3 Node node = NodeUtil.createPath(configSession.getRootNode(), "/server/MIMEMapping", NodeTypes.ContentNode.NAME);
146  3 Node jpg = node.addNode("jpg", NodeTypes.ContentNode.NAME);
147  3 jpg.setProperty("extension", "jpg");
148  3 jpg.setProperty("mime-type", "image/jpeg");
149   
150  3 Node png = node.addNode("png", NodeTypes.ContentNode.NAME);
151  3 png.setProperty("extension", "png");
152  3 png.setProperty("mime-type", "image/png");
153   
154    // set up the MIMEMapping cfg with a few MIME types
155  3 MIMEMapping.load();
156    }
157   
 
158  1 toggle @Test
159    public void requestForSimilarUncachedImageOnlyGeneratesItOnce() throws Exception {
160   
161    // ParameterProvider for tests - return a new instance of the same node everytime
162    // if we'd return the same src instance everytime, the purpose of this test would be null
163  1 final ParameterProviderFactory<Object, Node> ppf = new TestParameterProviderFactory(websiteSession, nodePath);
164   
165  1 final OutputFormat png = new OutputFormat();
166  1 png.setFormatName("png");
167   
168  1 final BufferedImage dummyImg = ImageIO.read(getClass().getResourceAsStream("/funnel.gif"));
169  1 assertNotNull("Couldn't load dummy test image", dummyImg);
170   
171  1 final ImageGenerator<ParameterProvider<Node>> generator = mock(ImageGenerator.class);
172  1 when(generator.getParameterProviderFactory()).thenReturn(ppf);
173  1 when(generator.getName()).thenReturn("test");
174  1 when(generator.getOutputFormat(isA(ParameterProvider.class))).thenReturn(png);
175   
176    // aaaaand finally, here's the real reason for this test !
177  1 when(generator.generate(isA(ParameterProvider.class))).thenReturn(dummyImg);
178   
179    // yeah, we're using a "fake" workspace for the image cache, to avoid having to setup a custom one in this test
180  1 final ImageStreamer streamer = newCachingImageStreamer(ppf, configSession);
181   
182    // Generator instances will always be the same (including paramProvFac)
183    // since they are instantiated with the module config and c2b.
184    // ParamProv is a new instance every time.
185    // streamer can (must) be the same - once single HM, one cache.
186   
187    // thread pool of 10, launching 8 requests, can we hit some concurrency please ?
188  1 final ExecutorService executor = Executors.newFixedThreadPool(10);
189  1 final ByteArrayOutputStream[] outs = new ByteArrayOutputStream[8];
190  1 final Future[] futures = new Future[8];
191  9 for (int i = 0; i < outs.length; i++) {
192  8 outs[i] = new ByteArrayOutputStream();
193  8 futures[i] = executor.submit(new TestJob(generator, streamer, outs[i]));
194    }
195  1 executor.shutdown();
196  1 executor.awaitTermination(30, TimeUnit.SECONDS);
197   
198  1 for (Future<?> future : futures) {
199  8 assertTrue(future.isDone());
200  8 assertFalse(future.isCancelled());
201    // ignore the results of TestJob - all we care about is if an exception was thrown
202    // and if there was any, it is kept in Future until we call Future.get()
203  8 future.get();
204    }
205   
206  1 final Node imageNode = configSession.getNode("/test/website/foo/bar/generated-image");
207    // update node meta data
208  1 Node cachedNode = configSession.getNode("/test/website/foo/bar");
209  1 NodeTypes.Activatable.update(cachedNode, "foo", true);
210  1 final InputStream res = imageNode.getProperty(JcrConstants.JCR_DATA).getBinary().getStream();
211  1 final ByteArrayOutputStream cachedOut = new ByteArrayOutputStream();
212  1 IOUtils.copy(res, cachedOut);
213   
214    // assert all outs are the same
215  8 for (int i = 1; i < outs.length; i++) {
216    // TODO assert they're all equals byte to byte to the source? or in size? can't as-is since we convert...
217  7 final byte[] a = outs[i - 1].toByteArray();
218  7 final byte[] b = outs[i].toByteArray();
219  7 assertTrue(a.length > 0);
220  7 assertEquals("Different sizes (" + Math.abs(a.length - b.length) + " bytes diff.) with i=" + i, a.length, b.length);
221  7 assertTrue("not equals for outs/" + i, Arrays.equals(a, b));
222  7 outs[i - 1] = null; // cleanup all those byte[], or we'll soon run out of memory
223    }
224  1 assertTrue("failed comparing last thread's result with what we got from hierarchyManager",
225    Arrays.equals(outs[outs.length - 1].toByteArray(), cachedOut.toByteArray()));
226  1 outs[outs.length - 1] = null;
227   
228    // now start again another bunch of requests... they should ALL get their results from the cache
229  1 final ExecutorService executor2 = Executors.newFixedThreadPool(10);
230  1 final ByteArrayOutputStream[] outs2 = new ByteArrayOutputStream[8];
231  1 final Future[] futures2 = new Future[8];
232  9 for (int i = 0; i < outs2.length; i++) {
233  8 outs2[i] = new ByteArrayOutputStream();
234  8 futures2[i] = executor2.submit(new TestJob(generator, streamer, outs2[i]));
235    }
236  1 executor2.shutdown();
237  1 executor2.awaitTermination(30, TimeUnit.SECONDS);
238   
239  1 for (Future<?> future : futures2) {
240  8 assertTrue(future.isDone());
241  8 assertFalse(future.isCancelled());
242    // ignore the results of TestJob - all we care about is if an exception was thrown
243    // and if there was any, it is kept in Future until we call Future.get()
244  8 future.get();
245    }
246   
247  1 final Node cachedNode2 = configSession.getNode("/test/website/foo/bar");
248  1 final InputStream res2 = cachedNode2.getNode("generated-image").getProperty(JcrConstants.JCR_DATA).getBinary().getStream();
249  1 final ByteArrayOutputStream cachedOut2 = new ByteArrayOutputStream();
250  1 IOUtils.copy(res2, cachedOut2);
251   
252    // assert all outs are the same
253  8 for (int i = 1; i < outs2.length; i++) {
254    // TODO assert they're all equals byte to byte to the source? or in size? can't as-is since we re-save..
255  7 final byte[] a = outs2[i - 1].toByteArray();
256  7 final byte[] b = outs2[i].toByteArray();
257  7 assertTrue(a.length > 0);
258  7 assertEquals("Different sizes (" + Math.abs(a.length - b.length) + " bytes diff.) with i=" + i, a.length, b.length);
259  7 assertTrue("not equals for outs2/" + i, Arrays.equals(a, b));
260  7 outs2[i - 1] = null;
261    }
262  1 assertTrue("failed comparing last thread's result with what we got from hierarchyManager",
263    Arrays.equals(outs2[outs2.length - 1].toByteArray(), cachedOut2.toByteArray()));
264   
265  1 outs2[outs2.length - 1] = null;
266    }
267   
 
268  1 toggle @Test
269    public void makeSureMIMETypeIsCorrectlyPersisted() throws Exception {
270    // GIVEN
271   
272    // ParameterProvider for tests - return a new instance of the same node everytime
273    // if we'd return the same src instance everytime, the purpose of this test would be null
274  1 final ParameterProviderFactory<Object, Node> ppf = new TestParameterProviderFactory(websiteSession, nodePath);
275   
276  1 final OutputFormat jpg = new OutputFormat();
277  1 jpg.setFormatName("jpg");
278   
279  1 final BufferedImage dummyImg = ImageIO.read(getClass().getResourceAsStream("/huffman.jpg"));
280  1 assertNotNull("Couldn't load dummy test image", dummyImg);
281   
282  1 final ImageGenerator<ParameterProvider<Node>> generator = mock(ImageGenerator.class);
283  1 when(generator.getParameterProviderFactory()).thenReturn(ppf);
284  1 when(generator.getName()).thenReturn("test");
285  1 when(generator.getOutputFormat(isA(ParameterProvider.class))).thenReturn(jpg);
286   
287  1 when(generator.generate(isA(ParameterProvider.class))).thenReturn(dummyImg);
288   
289    // yeah, we're using a "fake" workspace for the image cache, to avoid having to setup a custom one in this test
290  1 NodeUtil.createPath(configSession.getRootNode(), nodePath, NodeTypes.ContentNode.NAME);
291   
292  1 final CachingImageStreamer streamer = new CachingImageStreamer(configSession, ppf.getCachingStrategy(), new DefaultImageStreamer(), MgnlContext.getSystemContext());
293   
294    // WHEN
295  1 ParameterProvider pp = generator.getParameterProviderFactory().newParameterProviderFor(null);
296  1 streamer.generateAndStore(generator, pp);
297   
298  1 final Node imageNode = configSession.getNode("/test/website/foo/bar/generated-image");
299  1 assertThat(imageNode.getProperty(JcrConstants.JCR_MIMETYPE).getString(), is("image/jpeg"));
300    }
301   
 
302  1 toggle @Test
303    public void generateAndStoreIsDoneUnderSystemContext() throws RepositoryException, IOException, ImagingException {
304    // GIVEN
305   
306    // ParameterProvider for tests - return a new instance of the same node everytime
307    // if we'd return the same src instance everytime, the purpose of this test would be null
308  1 final ParameterProviderFactory<Object, Node> ppf = new TestParameterProviderFactory(websiteSession, nodePath);
309   
310  1 final OutputFormat png = new OutputFormat();
311  1 png.setFormatName("png");
312   
313  1 final BufferedImage dummyImg = ImageIO.read(getClass().getResourceAsStream("/funnel.gif"));
314  1 assertNotNull("Couldn't load dummy test image", dummyImg);
315   
316  1 final ImageGenerator<ParameterProvider<Node>> generator = mock(ImageGenerator.class);
317  1 when(generator.getParameterProviderFactory()).thenReturn(ppf);
318  1 when(generator.getName()).thenReturn("test");
319  1 when(generator.getOutputFormat(isA(ParameterProvider.class))).thenReturn(png);
320   
321  1 when(generator.generate(isA(ParameterProvider.class))).thenReturn(dummyImg);
322   
323    // yeah, we're using a "fake" workspace for the image cache, to avoid having to setup a custom one in this test
324  1 final CachingImageStreamer streamer = newCachingImageStreamer(ppf, configSession);
325   
326    // WHEN
327  1 streamer.generateAndStore(generator, generator.getParameterProviderFactory().newParameterProviderFor(null));
328   
329    // THEN
330  1 final Session systemSession = MgnlContext.getSystemContext().getJCRSession("config");
331  1 assertTrue(systemSession instanceof SingleSaveSessionWrapper);
332  1 assertTrue(configSession instanceof SingleSaveSessionWrapper);
333  1 assertTrue("should have saved in system session", ((SingleSaveSessionWrapper) systemSession).saved);
334  1 assertFalse("should not have saved in regular session", ((SingleSaveSessionWrapper) configSession).saved);
335    }
336   
 
337  0 toggle @Test
338    @Ignore
339    public void shouldStoreNothingWhenUsingNullCachingStrategy() throws IOException, ImagingException, RepositoryException {
340    // GIVEN
341  0 final HttpServletRequest req = mock(HttpServletRequest.class);
342  0 when(req.getRequestURI()).thenReturn("dummyUri");
343   
344  0 final ParameterProviderFactory<HttpServletRequest, String> ppf = new ParameterProviderFactory<HttpServletRequest, String>() {
 
345  0 toggle @Override
346    public ParameterProvider<String> newParameterProviderFor(HttpServletRequest request) {
347  0 return new StringParameterProvider(request.getRequestURI());
348    }
349   
 
350    toggle @Override
351    public CachingStrategy<String> getCachingStrategy() {
352    return new NoCachingStrategy<>();
353    }
354    };
355   
356  0 final OutputFormat png = new OutputFormat();
357  0 png.setFormatName("png");
358   
359  0 final BufferedImage dummyImg = ImageIO.read(getClass().getResourceAsStream("/funnel.gif"));
360  0 assertNotNull("Couldn't load dummy test image", dummyImg);
361   
362  0 final ImageGenerator<ParameterProvider<String>> generator = mock(ImageGenerator.class);
363  0 when(generator.getParameterProviderFactory()).thenReturn(ppf);
364  0 when(generator.getName()).thenReturn("test");
365  0 when(generator.getOutputFormat(isA(ParameterProvider.class))).thenReturn(png);
366  0 when(generator.generate(isA(ParameterProvider.class))).thenReturn(dummyImg);
367   
368    // yeah, we're using a "fake" workspace for the image cache, to avoid having to setup a custom one in this test
369  0 final Session config = MgnlContext.getJCRSession("config");
370  0 final CachingImageStreamer streamer = newCachingImageStreamer(ppf, config);
371   
372    // WHEN
373  0 final ByteArrayOutputStream out = new ByteArrayOutputStream();
374  0 streamer.serveImage(generator, ppf.newParameterProviderFor(req), new DummyImageResponse(out));
375   
376    // THEN
377    // Ensure we're still using the NCS
378  0 assertThat(ppf.getCachingStrategy(), instanceOf(NoCachingStrategy.class));
379   
380    // The image was served:
381  0 assertThat(out.size(), greaterThan(0));
382   
383    // Check nothing was stored
384  0 final Session session = MgnlContext.getJCRSession("config");
385  0 final Node root = session.getRootNode();
386    // Collect all nodes except /jcr:system node
387  0 final Iterable<Node> nodes = NodeUtil.collectAllChildren(root, Predicates.not(new NodeTypePredicate("rep:system")));
388  0 assertThat(nodes, emptyIterable());
389    }
390   
391    /**
392    * This test is not executed by default - too long !
393    * Used to reproduce the "session already closed issue", see MGNLIMG-59.
394    * Set the "expiration" property of the jobs map in CachingImageStreamer to a longer value
395    * to have more chances of reproducing the problem.
396    */
 
397  0 toggle @Ignore
398    @Test
399    public void concurrencyAndJCRSessions() throws Exception {
400    // ParameterProvider for tests - return a new instance of the same node everytime
401    // if we'd return the same src instance everytime, the purpose of this test would be null
402  0 final ParameterProviderFactory<Object, Node> ppf = new TestParameterProviderFactory(websiteSession, nodePath);
403   
404  0 final OutputFormat png = new OutputFormat();
405  0 png.setFormatName("png");
406   
407  0 final ImageOperationChain<ParameterProvider<Node>> generator = new ImageOperationChain<>();
408  0 final URLImageLoader<ParameterProvider<Node>> load = new URLImageLoader<>();
409  0 load.setUrl(getClass().getResource("/funnel.gif").toExternalForm());
410  0 generator.addOperation(load);
411  0 generator.setOutputFormat(png);
412  0 generator.setName("foo blob bar");
413  0 generator.setParameterProviderFactory(ppf);
414   
415    // yeah, we're using a "fake" workspace for the image cache, to avoid having to setup a custom one in this test
416  0 final Session config = MgnlContext.getJCRSession("config");
417   
418  0 final ImageStreamer streamer = newCachingImageStreamer(ppf, config);
419   
420    // thread pool of 10, launching 8 requests, can we hit some concurrency please ?
421  0 final ExecutorService executor = Executors.newFixedThreadPool(10);
422  0 final ByteArrayOutputStream[] outs = new ByteArrayOutputStream[8];
423  0 final Future[] futures = new Future[8];
424  0 for (int i = 0; i < outs.length; i++) {
425  0 final int ii = i;
426  0 outs[i] = new ByteArrayOutputStream();
427  0 futures[i] = executor.submit(new Runnable() {
 
428  0 toggle @Override
429    public void run() {
430  0 final ParameterProvider p = generator.getParameterProviderFactory().newParameterProviderFor(null);
431  0 try {
432  0 streamer.serveImage(generator, p, new DummyImageResponse(outs[ii]));
433    } catch (Exception e) {
434  0 throw new RuntimeException(e); // TODO
435    }
436    }
437    });
438    }
439  0 executor.shutdown();
440  0 executor.awaitTermination(30, TimeUnit.SECONDS);
441   
442  0 for (Future<?> future : futures) {
443  0 assertTrue(future.isDone());
444  0 assertFalse(future.isCancelled());
445    // ignore the results of TestJob - but if there was an exception thrown by TestJob.call(),
446    // it is only thrown back at us when we call get() below. (so the test will fail badly if the job threw an exception)
447  0 Object ignored = future.get();
448    }
449   
450  0 shutdownRepository(true);
451   
452    // sleep for a while so that the jobs map's expiration thread can kick in !
453  0 Thread.sleep(10000);
454    }
455   
456    // just a generation job for tests
 
457    private class TestJob implements Callable<Object> {
458    private final ImageGenerator generator;
459    private final ImageStreamer streamer;
460    private final OutputStream out;
461   
 
462  16 toggle public TestJob(ImageGenerator generator, ImageStreamer streamer, final OutputStream out) {
463  16 this.generator = generator;
464  16 this.streamer = streamer;
465  16 this.out = out;
466    }
467   
 
468  16 toggle @Override
469    public Object call() throws Exception {
470  16 MgnlContext.setInstance(Components.getComponent(SystemContext.class));
471  16 final ParameterProvider p = generator.getParameterProviderFactory().newParameterProviderFor(null);
472  16 streamer.serveImage(generator, p, new DummyImageResponse(out));
473  16 return null;
474    }
475    }
476   
477    // TODO - this is an ugly hack to workaround MAGNOLIA-2593 - we should review RepositoryTestCase
 
478  3 toggle @Override
479    protected void initDefaultImplementations() throws IOException, ModuleManagementException {
480    //MgnlTestCase clears factory before running this method, so we have to instrument factory here rather then in setUp() before calling super.setUp()
481  3 ComponentsTestUtil.setImplementation(SystemContext.class, MockContext.class);
482  3 ComponentsTestUtil.setImplementation(RepositoryManager.class, MockRepositoryManager.class);
483  3 ModuleRegistry registry = mock(ModuleRegistry.class);
484  3 ComponentsTestUtil.setInstance(ModuleRegistry.class, registry);
485   
486  3 final ModuleDefinitionReader fakeReader = new ModuleDefinitionReader() {
 
487  0 toggle @Override
488    public ModuleDefinition read(Reader in) throws ModuleManagementException {
489  0 return null;
490    }
491   
 
492  0 toggle @Override
493    public Map readAll() throws ModuleManagementException {
494  0 Map m = new HashMap();
495  0 m.put("moduleDef", "dummy");
496  0 return m;
497    }
498   
 
499  0 toggle @Override
500    public ModuleDefinition readFromResource(String resourcePath) throws ModuleManagementException {
501  0 return null;
502    }
503    };
504  3 final ModuleManagerImpl fakeModuleManager = new ModuleManagerImpl(null, fakeReader, null, null, null, null) {
 
505  0 toggle @Override
506    public List loadDefinitions() throws ModuleManagementException {
507    // TODO Auto-generated method stub
508  0 return new ArrayList();
509    }
510    };
511  3 ComponentsTestUtil.setInstance(ModuleManager.class, fakeModuleManager);
512  3 super.initDefaultImplementations();
513    }
514   
515    /*
516    final IMocksControl iMocksControl = createStrictControl();
517    // can't be strict on method call order on hm, because we can't guarantee which of the 8 threads will create the node
518    iMocksControl.checkOrder(false);
519    // not exactly sure what this entails; hopefully it doesn't render the test useless...
520    iMocksControl.makeThreadSafe(true);
521    final HierarchyManager hm = iMocksControl.createMock(HierarchyManager.class);
522    final Content root = createStrictMock(Content.class);
523    final Content t = createStrictMock(Content.class);
524    final Content m = createStrictMock(Content.class);
525    final Content p = createStrictMock(Content.class);
526    final Content y = createStrictMock(Content.class);
527   
528    // first 8 request: node doesn't exist.
529    for (int i = 0; i < 8; i++) {
530    // generator name + path provided by ParameterProviderFactory
531    expect(hm.isExist("/test/my/param/yo")).andReturn(false);
532    }
533   
534    // one of these 8 threads creates the node
535    expect(hm.getRoot()).andReturn(root);
536    expect(root.hasContent("test")).andReturn(false);
537    expect(root.createContent("test", ItemType.CONTENT)).andReturn(t);
538    expect(t.hasContent("my")).andReturn(false);
539    expect(t.createContent("my", ItemType.CONTENT)).andReturn(m);
540    expect(m.hasContent("param")).andReturn(false);
541    expect(m.createContent("param", ItemType.CONTENT)).andReturn(p);
542    expect(p.hasContent("yo")).andReturn(false);
543    expect(p.createContent("yo", ItemType.CONTENT)).andReturn(y);
544    expect(y.hasNodeData("generated-image")).andReturn(false);
545   
546    // 8 more requests, the node exists in the hm
547    for (int i = 0; i < 8; i++) {
548    expect(hm.isExist("/test/my/param/yo")).andReturn(true);
549    }
550   
551    replay(hm, root, t, m, p, y);
552    */
553   
 
554  2 toggle private CachingImageStreamer newCachingImageStreamer(ParameterProviderFactory<?, ?> ppf, Session session) {
555  2 return new CachingImageStreamer(session, ppf.getCachingStrategy(), new DefaultImageStreamer(), Components.getComponent(SystemContext.class)) {
 
556  2 toggle @Override
557    protected MediaType getMediaType(OutputFormat outputFormat) {
558    // getMediaType in DefaultImageStreamer
559  2 return MediaType.PNG;
560    }
561    };
562    }
563   
564    /**
565    * A JCRSessionStrategy which allows returning a given session instead of the regular one.
566    */
 
567    private static class DelegatingSessionStrategy implements JCRSessionStrategy {
568    private final Map<String, Session> jcrSessions = new HashMap<>();
569    private final JCRSessionStrategy delegate;
570   
 
571  6 toggle DelegatingSessionStrategy(JCRSessionStrategy delegate) {
572  6 this.delegate = delegate;
573    }
574   
 
575  54 toggle @Override
576    public Session getSession(String workspaceName) throws RepositoryException {
577  54 if (jcrSessions.containsKey(workspaceName)) {
578  39 final Session session = jcrSessions.get(workspaceName);
579  39 assertThat(session, is(instanceOf(SingleSaveSessionWrapper.class)));
580  39 return session;
581    } else {
582  15 final Session session = delegate.getSession(workspaceName);
583  15 assertThat(session, is(not(instanceOf(SingleSaveSessionWrapper.class))));
584  15 final SingleSaveSessionWrapper sessionWrapper = new SingleSaveSessionWrapper(session);
585  15 jcrSessions.put(workspaceName, sessionWrapper);
586  15 return sessionWrapper;
587    }
588    }
589   
 
590  3 toggle @Override
591    public void release() {
592  3 delegate.release();
593    }
594    }
595   
 
596    private static class TestParameterProviderFactory implements ParameterProviderFactory {
597    private final Session session;
598    private final String srcPath;
599   
 
600  3 toggle public TestParameterProviderFactory(Session session, String srcPath) {
601  3 this.session = session;
602  3 this.srcPath = srcPath;
603    }
604   
605   
 
606  18 toggle @Override
607    public ParameterProvider<Node> newParameterProviderFor(Object environment) {
608  18 try {
609  18 final Node node = session.getNode(srcPath);
610  18 return new BinaryNodeParameterProvider(new SimpleEqualityNodeWrapper(node));
611    } catch (RepositoryException e) {
612  0 throw new RuntimeException(e);
613    }
614    }
615   
 
616    toggle @Override
617    public CachingStrategy getCachingStrategy() {
618    return new NodeBasedCachingStrategy();
619    }
620    }
621   
 
622    private static class SingleSaveSessionWrapper extends DelegateSessionWrapper {
623    boolean saved = false;
624   
 
625  15 toggle public SingleSaveSessionWrapper(Session session) throws RepositoryException {
626  15 super(session);
627    }
628   
 
629  6 toggle @Override
630    public synchronized void save() throws RepositoryException {
631  6 if (saved) {
632  0 fail("save() was called more than once");
633    } else {
634  6 saved = true;
635    }
636  6 super.save();
637    }
638    }
639    }