com.ibm.jaggr.core.impl.cache.GzipCacheImplTest.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.jaggr.core.impl.cache.GzipCacheImplTest.java

Source

/*
 * (C) Copyright 2012, IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ibm.jaggr.core.impl.cache;

import com.ibm.jaggr.core.IAggregator;
import com.ibm.jaggr.core.cache.ICacheManager;
import com.ibm.jaggr.core.cache.IGzipCache;
import com.ibm.jaggr.core.executors.IExecutors;
import com.ibm.jaggr.core.impl.executors.ExecutorsImpl;
import com.ibm.jaggr.core.test.TestUtils;
import com.ibm.jaggr.core.util.CopyUtil;

import com.google.common.io.Files;

import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableInt;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.powermock.reflect.Whitebox;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.zip.GZIPInputStream;

public class GzipCacheImplTest {

    static final String testData = "This is a test.";
    static final String newTestData = "This is a new test";
    static final String newTestData2 = "This is a new new test";
    File tempdir;
    File tempfile;
    CountDownLatch latch1;
    CountDownLatch latch2;
    IAggregator mockAggregator;
    ICacheManager mockCacheManager;
    IExecutors executors;
    List<String> deletedCacheFiles;

    @Before
    public void setup() throws IOException {
        latch1 = new CountDownLatch(1);
        latch2 = new CountDownLatch(1);
        tempdir = Files.createTempDir();
        tempfile = new File(tempdir, "source");
        deletedCacheFiles = new ArrayList<String>();
        FileWriter writer = new FileWriter(tempfile);
        writer.append(testData);
        writer.close();
        mockAggregator = EasyMock.createNiceMock(IAggregator.class);
        mockCacheManager = EasyMock.createNiceMock(ICacheManager.class);
        executors = new ExecutorsImpl(null, null, null, null);
        EasyMock.expect(mockAggregator.getCacheManager()).andReturn(mockCacheManager).anyTimes();
        EasyMock.expect(mockAggregator.getExecutors()).andAnswer(new IAnswer<IExecutors>() {
            @Override
            public IExecutors answer() throws Throwable {
                return executors;
            }
        }).anyTimes();
        EasyMock.expect(mockCacheManager.getCacheDir()).andReturn(tempdir).anyTimes();
        mockCacheManager.createCacheFileAsync(EasyMock.isA(String.class), EasyMock.isA(InputStream.class),
                EasyMock.isA(ICacheManager.CreateCompletionCallback.class));
        EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
            @Override
            public Object answer() throws Throwable {
                final String prefix = (String) EasyMock.getCurrentArguments()[0];
                final InputStream in = (InputStream) EasyMock.getCurrentArguments()[1];
                final ICacheManager.CreateCompletionCallback callback = (ICacheManager.CreateCompletionCallback) EasyMock
                        .getCurrentArguments()[2];
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            latch1.await();
                            File cacheFile = File.createTempFile(prefix, ".cache", tempdir);
                            CopyUtil.copy(in, new FileOutputStream(cacheFile));
                            callback.completed(cacheFile.getName(), null);
                            latch2.countDown();
                        } catch (Exception ex) {
                            callback.completed(null, ex);
                        }
                    }
                }).start();
                return null;
            }
        }).anyTimes();
        mockCacheManager.deleteFileDelayed(EasyMock.isA((String.class)));
        EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
            @Override
            public Object answer() throws Throwable {
                deletedCacheFiles.add((String) EasyMock.getCurrentArguments()[0]);
                return null;
            }
        }).anyTimes();
    }

    @After
    public void teardown() throws Exception {
        if (tempdir != null) {
            TestUtils.deleteRecursively(tempdir);
            tempdir = null;
        }
    }

    @Test
    public void testGetInputStream() throws Exception {

        GzipCacheImpl impl = new GzipCacheImpl();

        EasyMock.replay(mockAggregator, mockCacheManager);
        impl.setAggregator(mockAggregator);
        MutableInt retLength = new MutableInt();

        // Get the input stream.  Should be a ByteArrayInputStream until
        // the async thread waiting on latch1 gets to write the cache file.
        InputStream is = impl.getInputStream("key", tempfile.toURI(), retLength);
        Assert.assertTrue(is instanceof ByteArrayInputStream);

        // Validate the gzipped data
        Assert.assertEquals(testData, unzipInputStream(is, retLength.getValue()));

        // Validate the cache entry fields
        IGzipCache.ICacheEntry cacheEntry = impl.get("key");
        Assert.assertNotNull(Whitebox.getInternalState(cacheEntry, "bytes"));
        Assert.assertEquals(Long.valueOf(tempfile.lastModified()),
                (Long) Whitebox.getInternalState(cacheEntry, "lastModified"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "ex"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "file"));

        // Request the input stream again.  Make sure we get a new ByteArrayInputStream
        // to the same content.
        is = impl.getInputStream("key", tempfile.toURI(), retLength);
        Assert.assertTrue(is instanceof ByteArrayInputStream);
        Assert.assertEquals(testData, unzipInputStream(is, retLength.getValue()));

        // now release the cache file writer thread and wait for it to complete
        latch1.countDown();
        latch2.await();

        // The input stream this time should be a FileInputStream
        is = impl.getInputStream("key", tempfile.toURI(), retLength);
        Assert.assertTrue(is instanceof FileInputStream);
        // validate the data
        Assert.assertEquals(testData, unzipInputStream(is, retLength.getValue()));

        // validate the cache entry to make sure the fields were updated as expected
        Assert.assertSame(cacheEntry, impl.get("key"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "bytes"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "ex"));
        File cacheFile = (File) Whitebox.getInternalState(cacheEntry, "file");
        Assert.assertTrue(cacheFile.getName().startsWith("source.gzip."));
        Assert.assertTrue(cacheFile.getName().endsWith(".cache"));
        Assert.assertEquals(tempfile.lastModified(), cacheFile.lastModified());
    }

    @Test
    public void testGetInputStreamLastModified() throws Exception {
        GzipCacheImpl impl = new GzipCacheImpl();
        latch2 = new CountDownLatch(2);
        long oldLastMod = new Date().getTime() - 10000;
        tempfile.setLastModified(oldLastMod);

        EasyMock.replay(mockAggregator, mockCacheManager);
        impl.setAggregator(mockAggregator);
        MutableInt retLength = new MutableInt();

        // Get the input stream.  Should be a ByteArrayInputStream until
        // the async thread waiting on latch1 gets to write the cache file.
        InputStream is = impl.getInputStream("key", tempfile.toURI(), retLength);
        Assert.assertTrue(is instanceof ByteArrayInputStream);

        // Validate the gzipped data
        Assert.assertEquals(testData, unzipInputStream(is, retLength.getValue()));

        // Validate the cache entry fields
        IGzipCache.ICacheEntry cacheEntry = impl.get("key");
        Assert.assertNotNull(Whitebox.getInternalState(cacheEntry, "bytes"));
        Assert.assertEquals(Long.valueOf(tempfile.lastModified()),
                (Long) Whitebox.getInternalState(cacheEntry, "lastModified"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "ex"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "file"));

        // Now update the last-modified date of the input resource
        FileWriter writer = new FileWriter(tempfile);
        writer.append(newTestData);
        writer.close();

        // Get the input stream again and ensure it returns the updated contents
        is = impl.getInputStream("key", tempfile.toURI(), retLength);
        Assert.assertTrue(is instanceof ByteArrayInputStream);
        Assert.assertEquals(newTestData, unzipInputStream(is, retLength.getValue()));

        // Validate the cache entry fields
        Assert.assertNotSame(cacheEntry, impl.get("key"));
        cacheEntry = impl.get("key");
        Assert.assertNotNull(Whitebox.getInternalState(cacheEntry, "bytes"));
        Assert.assertEquals(Long.valueOf(tempfile.lastModified()),
                (Long) Whitebox.getInternalState(cacheEntry, "lastModified"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "ex"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "file"));

        // now write the cache file to disk
        // now release the cache file writer thread and wait for it to complete
        latch1.countDown();
        latch2.await();

        // The input stream this time should be a FileInputStream
        is = impl.getInputStream("key", tempfile.toURI(), retLength);
        Assert.assertTrue(is instanceof FileInputStream);
        // validate the data
        Assert.assertEquals(newTestData, unzipInputStream(is, retLength.getValue()));

        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "bytes"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "ex"));
        File cacheFile1 = (File) Whitebox.getInternalState(cacheEntry, "file");
        Assert.assertTrue(cacheFile1.getName().startsWith("source.gzip."));
        Assert.assertTrue(cacheFile1.getName().endsWith(".cache"));
        long newTestDataLastMod = cacheFile1.lastModified();
        Assert.assertTrue("oldLastMod = " + oldLastMod + ", newTestDataLastMod = " + newTestDataLastMod,
                Math.abs(oldLastMod - newTestDataLastMod) >= 9000 /* account for rounding on unix */);

        // reset the latches
        latch1 = new CountDownLatch(1);
        latch2 = new CountDownLatch(1);
        // Now update the last-modified date of the input resource
        writer = new FileWriter(tempfile);
        writer.append(newTestData2);
        writer.close();
        tempfile.setLastModified(new Date().getTime() + 10000);

        // The input stream this time should be a ByteArrayInputStream
        is = impl.getInputStream("key", tempfile.toURI(), retLength);
        Assert.assertTrue(is instanceof ByteArrayInputStream);
        Assert.assertNotSame(cacheEntry, impl.get("key"));
        cacheEntry = impl.get("key");
        // validate the data
        Assert.assertEquals(newTestData2, unzipInputStream(is, retLength.getValue()));
        Assert.assertEquals(tempfile.lastModified(), Whitebox.getInternalState(cacheEntry, "lastModified"));

        // now write the cache file to disk
        // now release the cache file writer thread and wait for it to complete
        latch1.countDown();
        latch2.await();

        is = impl.getInputStream("key", tempfile.toURI(), retLength);
        Assert.assertTrue(is instanceof FileInputStream);
        Assert.assertSame(cacheEntry, impl.get("key"));
        Assert.assertEquals(newTestData2, unzipInputStream(is, retLength.getValue()));

        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "bytes"));
        Assert.assertNull(Whitebox.getInternalState(cacheEntry, "ex"));
        File cacheFile2 = (File) Whitebox.getInternalState(cacheEntry, "file");
        Assert.assertTrue(cacheFile2.getName().startsWith("source.gzip."));
        Assert.assertTrue(cacheFile2.getName().endsWith(".cache"));
        Assert.assertFalse(cacheFile1.getName().equals(cacheFile2.getName()));
        long newTestData2LastMod = cacheFile2.lastModified();
        Assert.assertTrue(
                "newTestDataLastMod = " + newTestDataLastMod + ", newTestData2LastMod = " + newTestData2LastMod,
                Math.abs(newTestDataLastMod - newTestData2LastMod) >= 9000 /* account for rounding on unix */);
        Assert.assertEquals(cacheFile1.getName(), deletedCacheFiles.get(0));

    }

    @SuppressWarnings("unchecked")
    @Test
    public void testGetInputStreamExceptionHandling() throws Exception {
        final GzipCacheImpl impl = new GzipCacheImpl();
        EasyMock.replay(mockAggregator, mockCacheManager);
        impl.setAggregator(mockAggregator);
        final MutableInt retLength = new MutableInt();
        final CountDownLatch latch3 = new CountDownLatch(1);

        EasyMock.reset(mockCacheManager);
        EasyMock.expect(mockCacheManager.getCacheDir()).andReturn(tempdir).anyTimes();
        mockCacheManager.createCacheFileAsync(EasyMock.isA(String.class), EasyMock.isA(InputStream.class),
                EasyMock.isA(ICacheManager.CreateCompletionCallback.class));
        EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
            @Override
            public Object answer() throws Throwable {
                latch1.countDown();
                latch2.await();
                throw new IOException("test generated exception");
            }
        }).anyTimes();
        EasyMock.replay(mockCacheManager);

        // get input stream (should throw execption because file was deleted
        final MutableBoolean exceptionCaught = new MutableBoolean(false);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    InputStream is = impl.getInputStream("key", tempfile.toURI(), retLength);
                } catch (Exception ex) {
                    exceptionCaught.setTrue();
                }
                latch3.countDown();
            }
        }).start();

        latch1.await();
        IGzipCache.ICacheEntry cacheEntry = impl.get("key");
        latch2.countDown();
        latch3.await();

        Assert.assertTrue(exceptionCaught.isTrue());
        Assert.assertNotNull(Whitebox.getInternalState(cacheEntry, "ex"));
        Assert.assertNull(impl.get("key"));

    }

    private String unzipInputStream(InputStream in, int expectedLength) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        CopyUtil.copy(in, bos);
        byte[] bytes = bos.toByteArray();
        Assert.assertEquals(bytes.length, expectedLength);
        ByteArrayOutputStream unzippedBos = new ByteArrayOutputStream();
        CopyUtil.copy(new GZIPInputStream(new ByteArrayInputStream(bytes)), unzippedBos);
        return new String(unzippedBos.toByteArray());
    }
}