/**
* Copyright (c) Codice Foundation
* <p/>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p/>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package ddf.catalog.resource.download;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.activation.MimeType;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import com.google.common.io.CountingOutputStream;
import ddf.catalog.cache.MockInputStream;
import ddf.catalog.cache.impl.ResourceCache;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.BasicTypes;
import ddf.catalog.event.retrievestatus.DownloadStatusInfoImpl;
import ddf.catalog.event.retrievestatus.DownloadsStatusEventListener;
import ddf.catalog.event.retrievestatus.DownloadsStatusEventPublisher;
import ddf.catalog.event.retrievestatus.DownloadsStatusEventPublisher.ProductRetrievalStatus;
import ddf.catalog.operation.ResourceRequest;
import ddf.catalog.operation.ResourceResponse;
import ddf.catalog.resource.Resource;
import ddf.catalog.resource.ResourceNotFoundException;
import ddf.catalog.resource.ResourceNotSupportedException;
import ddf.catalog.resourceretriever.ResourceRetriever;
public class ReliableResourceDownloaderTest {
private static final String DOWNLOAD_ID = "123";
public static String productCacheDirectory;
private static String productInputFilename;
private Resource mockResource;
private ResourceResponse mockResponse;
private MockInputStream mis;
private InputStream mockStream = mock(InputStream.class);
private ReliableResourceDownloaderConfig downloaderConfig;
private DownloadsStatusEventPublisher mockPublisher = mock(DownloadsStatusEventPublisher.class);
private Metacard mockMetacard;
@BeforeClass
public static void oneTimeSetup() throws IOException {
String workingDir = System.getProperty("user.dir");
productCacheDirectory = workingDir + "/target/tests/product-cache";
productInputFilename = workingDir + "/src/test/resources/foo_10_lines.txt";
File productInputFile = new File(productInputFilename);
}
@Before
public void setUp() {
downloaderConfig = new ReliableResourceDownloaderConfig();
downloaderConfig.setEventListener(mock(DownloadsStatusEventListener.class));
downloaderConfig.setEventPublisher(mockPublisher);
// Don't wait between attempts
downloaderConfig.setDelayBetweenAttemptsMS(0);
mockMetacard = getMockMetacard(DOWNLOAD_ID, "sauce");
}
@Test
public void testIOExceptionDuringRead() throws Exception {
ResourceResponse mockResponse = getMockResourceResponse(mockStream);
when(mockStream.read(any(byte[].class))).thenThrow(new IOException());
int retries = 5;
downloaderConfig.setMaxRetryAttempts(retries);
ReliableResourceDownloader downloader = new ReliableResourceDownloader(downloaderConfig,
new AtomicBoolean(), DOWNLOAD_ID, mockResponse, getMockRetriever());
downloader.setupDownload(mockMetacard, new DownloadStatusInfoImpl());
downloader.run();
verify(mockPublisher, times(retries)).postRetrievalStatus(any(ResourceResponse.class),
eq(ProductRetrievalStatus.RETRYING), any(Metacard.class), anyString(), anyLong(),
eq(DOWNLOAD_ID));
}
@Test
public void testCacheExceptionDuringWrite() throws Exception {
downloaderConfig.setCacheEnabled(true);
ResourceCache mockCache = mock(ResourceCache.class);
when(mockCache.isPending(anyString())).thenReturn(false);
when(mockCache.getProductCacheDirectory()).thenReturn(productCacheDirectory);
downloaderConfig.setResourceCache(mockCache);
mis = new MockInputStream(productInputFilename);
ResourceResponse mockResponse = getMockResourceResponse(mis);
ReliableResourceDownloader downloader = new ReliableResourceDownloader(downloaderConfig,
new AtomicBoolean(), "123", mockResponse, getMockRetriever());
downloader.setupDownload(mockMetacard, new DownloadStatusInfoImpl());
FileOutputStream mockFos = mock(FileOutputStream.class);
doThrow(new IOException()).when(mockFos).write(any(byte[].class), anyInt(), anyInt());
downloader.setFileOutputStream(mockFos);
downloader.run();
verify(mockPublisher, times(1)).postRetrievalStatus(any(ResourceResponse.class),
eq(ProductRetrievalStatus.RETRYING), any(Metacard.class), anyString(), anyLong(),
eq(DOWNLOAD_ID));
verify(mockCache, times(1)).removePendingCacheEntry(anyString());
assertThat(downloaderConfig.isCacheEnabled(), is(false));
}
@Test
@Ignore
// Can't figure out how to throw IOExcetion from CountingOutputStream
public void testClientOutputStreamException() throws Exception {
downloaderConfig.setCacheEnabled(true);
ResourceCache mockCache = mock(ResourceCache.class);
when(mockCache.isPending(anyString())).thenReturn(false);
when(mockCache.getProductCacheDirectory()).thenReturn(productCacheDirectory);
downloaderConfig.setResourceCache(mockCache);
mis = new MockInputStream(productInputFilename);
ResourceResponse mockResponse = getMockResourceResponse(mis);
ReliableResourceDownloader downloader = new ReliableResourceDownloader(downloaderConfig,
new AtomicBoolean(), "123", mockResponse, getMockRetriever());
downloader.setupDownload(mockMetacard, new DownloadStatusInfoImpl());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CountingOutputStream mockCountingFbos = new CountingOutputStream(baos);
IOUtils.closeQuietly(baos);
downloader.setCountingOutputStream(mockCountingFbos);
downloader.run();
verify(mockPublisher, times(1)).postRetrievalStatus(any(ResourceResponse.class),
eq(ProductRetrievalStatus.CANCELLED), any(Metacard.class), anyString(), anyLong(),
eq(DOWNLOAD_ID));
verify(mockCache, times(1)).removePendingCacheEntry(anyString());
assertThat(downloaderConfig.isCacheEnabled(), is(false));
}
private Metacard getMockMetacard(String id, String source) {
Metacard metacard = mock(Metacard.class);
when(metacard.getId()).thenReturn(id);
when(metacard.getSourceId()).thenReturn(source);
when(metacard.getMetacardType()).thenReturn(BasicTypes.BASIC_METACARD);
return metacard;
}
private ResourceResponse getMockResourceResponse(InputStream stream) throws Exception {
ResourceRequest resourceRequest = mock(ResourceRequest.class);
Map<String, Serializable> requestProperties = new HashMap<String, Serializable>();
when(resourceRequest.getPropertyNames()).thenReturn(requestProperties.keySet());
mockResource = mock(Resource.class);
when(mockResource.getInputStream()).thenReturn(stream);
when(mockResource.getName()).thenReturn("test-resource");
when(mockResource.getMimeType()).thenReturn(new MimeType("text/plain"));
mockResponse = mock(ResourceResponse.class);
when(mockResponse.getRequest()).thenReturn(resourceRequest);
when(mockResponse.getResource()).thenReturn(mockResource);
Map<String, Serializable> responseProperties = new HashMap<String, Serializable>();
when(mockResponse.getProperties()).thenReturn(responseProperties);
return mockResponse;
}
private ResourceRetriever getMockRetriever()
throws ResourceNotFoundException, ResourceNotSupportedException, IOException {
ResourceRetriever retriever = mock(ResourceRetriever.class);
when(retriever.retrieveResource(anyLong())).thenReturn(mockResponse);
return retriever;
}
}