/* This file is part of RouteConverter. RouteConverter is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. RouteConverter 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 General Public License for more details. You should have received a copy of the GNU General Public License along with RouteConverter; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Copyright (C) 2007 Christian Pesch. All Rights Reserved. */ package slash.navigation.download; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import slash.common.type.CompactCalendar; import slash.navigation.rest.Get; import slash.navigation.rest.Head; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import java.io.*; import java.util.List; import java.util.logging.Logger; import static java.io.File.createTempFile; import static java.lang.System.currentTimeMillis; import static java.util.Collections.singletonList; import static org.junit.Assert.*; import static slash.common.TestCase.calendar; import static slash.common.io.InputOutput.readBytes; import static slash.common.io.Transfer.UTF8_ENCODING; import static slash.common.type.CompactCalendar.fromMillis; import static slash.navigation.download.Action.*; import static slash.navigation.download.DownloadManager.WAIT_TIMEOUT; import static slash.navigation.download.State.*; public class DownloadManagerIT { private static final Logger log = Logger.getLogger(DownloadManagerIT.class.getName()); private static final String DOWNLOAD = System.getProperty("download", "http://static.routeconverter.com/test/"); private static final String LOREM_IPSUM_DOLOR_SIT_AMET = "Lorem ipsum dolor sit amet"; private static final String EXPECTED = LOREM_IPSUM_DOLOR_SIT_AMET + ", consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n" + "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"; private static final CompactCalendar LAST_MODIFIED = calendar(2015, 1, 3, 9, 9, 19); private static final long CONTENT_LENGTH = 447L; private static final String ETAG = "\"1bf-50bbbcff309d2-gzip\""; private static final String SHA1 = "597D5107C0DC296DF4F6128257F6F8D2079FA11A"; private static final CompactCalendar ZIP_LAST_MODIFIED = fromMillis(1394029600000L); private static final long ZIP_CONTENT_LENGTH = 415L; private static final String ZIP_SHA1 = "483AF8EEF96B20864776F019D4537B3750C1173D"; private static final CompactCalendar EXTRACTED_LAST_MODIFIED = fromMillis(1387698494000L); private DownloadManager manager; private File target, queueFile; private String readFileToString(File file) throws IOException { return new String(readBytes(new FileInputStream(file)), UTF8_ENCODING); } private void writeStringToFile(File file, String string) throws FileNotFoundException { PrintWriter writer = new PrintWriter(new FileOutputStream(file)); writer.print(string); writer.close(); assertEquals(string.length(), file.length()); } private void delete(String path) { File toDelete = new File(target.getParentFile(), path); if (toDelete.exists()) assertTrue(toDelete.delete()); } @Before public void setUp() throws IOException { queueFile = createTempFile("queueFile", ".xml"); manager = new DownloadManager(queueFile); target = createTempFile("local", ".txt"); delete("first/second/447bytes.txt"); delete("first/second"); delete("first"); delete("447bytes.txt"); } @After public void tearDown() { if (target.exists()) assertTrue(target.delete()); if (queueFile.exists()) if(!queueFile.delete()) queueFile.deleteOnExit(); manager.dispose(); } private static final Object notificationMutex = new Object(); void waitFor(final Download download, final State expectedState) { final boolean[] found = new boolean[1]; found[0] = false; TableModelListener l = new TableModelListener() { public void tableChanged(TableModelEvent e) { log.warning("Expected state: " + expectedState + ", download state: " + download.getState()); if (expectedState.equals(download.getState())) { synchronized (notificationMutex) { found[0] = true; notificationMutex.notifyAll(); } } } }; long start = currentTimeMillis(); manager.getModel().addTableModelListener(l); try { while (true) { synchronized (notificationMutex) { if (found[0]) break; if (currentTimeMillis() - start > WAIT_TIMEOUT) throw new IllegalStateException("waited for " + WAIT_TIMEOUT + " seconds without seeing " + expectedState); try { notificationMutex.wait(1000); } catch (InterruptedException e) { // intentionally left empty } } } } finally { manager.getModel().removeTableModelListener(l); } } @Test public void testInvalidUrl() throws IOException { Download download = manager.queueForDownload("Does not exist", DOWNLOAD + "doesntexist.txt", Copy, new FileAndChecksum(target, null), null); waitFor(download, Failed); assertEquals(Failed, download.getState()); } @Test public void testFreshDownload() throws IOException { assertTrue(target.delete()); Download download = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, null), null); waitFor(download, Succeeded); assertEquals(Succeeded, download.getState()); String actual = readFileToString(target); assertEquals(EXPECTED, actual); } @Ignore @Test public void testSetLastModifiedForLocalFiles() { File txt = new File("C:\\p4\\RouteSite\\static\\test\\447bytes.txt"); assertTrue(txt.setLastModified(LAST_MODIFIED.getTimeInMillis())); File zip = new File("C:\\p4\\RouteSite\\static\\test\\447bytes.zip"); assertTrue(zip.setLastModified(ZIP_LAST_MODIFIED.getTimeInMillis())); } @Ignore @Test public void testMapServerHEADAndGET() throws IOException { String[] URLS = new String[]{ // "http://localhost:8000/datasources/edition/online.xml" "http://www.androidmaps.co.uk/maps/africa/botswana.map", "http://download.freizeitkarte-osm.de/android/1404/freizeitkarte_berlin.map.zip", // Content-Type: application/zip "http://download.mapsforge.org/maps/asia/azerbaijan.map", "http://ftp5.gwdg.de/pub/misc/openstreetmap/openandromaps/maps/Germany/berlin.zip" // Content-Type: application/zip }; for (String url : URLS) { Head head200 = new Head(url); head200.executeAsString(); assertTrue(head200.isOk()); assertTrue(head200.getAcceptByteRanges()); assertNotNull(head200.getETag()); assertNotNull(head200.getLastModified()); assertNotNull(head200.getContentLength()); log.info(url + ":\nHEAD 200: " + head200.getHeaders()); Head head304IfModifiedSince = new Head(url); head304IfModifiedSince.setIfModifiedSince(head200.getLastModified()); head304IfModifiedSince.executeAsString(); assertTrue(head304IfModifiedSince.isNotModified()); assertFalse(head304IfModifiedSince.getAcceptByteRanges()); assertNotNull(head304IfModifiedSince.getETag()); assertNull(head304IfModifiedSince.getLastModified()); assertNull(head304IfModifiedSince.getContentLength()); log.info("Headers: " + head304IfModifiedSince.getHeaders()); Head head304Etag = new Head(url); head304Etag.setIfNoneMatch(head200.getETag()); head304Etag.executeAsString(); assertTrue(head304Etag.isNotModified()); assertFalse(head304Etag.getAcceptByteRanges()); assertNotNull(head304Etag.getETag()); assertNull(head304Etag.getLastModified()); assertNull(head304Etag.getContentLength()); log.info("Headers: " + head304Etag.getHeaders()); Get get200 = new Get(url); get200.executeAsString(); assertTrue(get200.isOk()); assertTrue(get200.getAcceptByteRanges()); assertNotNull(get200.getETag()); assertNotNull(get200.getLastModified()); assertNotNull(get200.getContentLength()); log.info("GET 200: " + get200.getHeaders()); Get get304IfModifiedSince = new Get(url); get304IfModifiedSince.setIfModifiedSince(head200.getLastModified()); get304IfModifiedSince.executeAsString(); assertTrue(get304IfModifiedSince.isNotModified()); assertFalse(get304IfModifiedSince.getAcceptByteRanges()); assertNotNull(get304IfModifiedSince.getETag()); assertNull(get304IfModifiedSince.getLastModified()); assertNull(get304IfModifiedSince.getContentLength()); log.info("Headers: " + get304IfModifiedSince.getHeaders()); Get get304Etag = new Get(url); get304Etag.setIfNoneMatch(head200.getETag()); get304Etag.executeAsString(); assertTrue(get304Etag.isNotModified()); assertFalse(get304Etag.getAcceptByteRanges()); assertNotNull(get304Etag.getETag()); assertNull(get304Etag.getLastModified()); assertNull(get304Etag.getContentLength()); log.info(get304Etag.getHeaders() + "\n"); } } @Test public void testHeadWithoutETag() throws IOException { assertTrue(target.delete()); Download download = manager.queueForDownload("HEAD for 447 Bytes", DOWNLOAD + "447bytes.txt", Head, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, CONTENT_LENGTH, SHA1)), null); waitFor(download, Succeeded); assertEquals(Succeeded, download.getState()); assertFalse(target.exists()); } @Test public void testHeadWithETag() throws IOException { assertTrue(target.delete()); Download download = manager.queueForDownload("HEAD for 447 Bytes", DOWNLOAD + "447bytes.txt", Head, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, CONTENT_LENGTH, SHA1)), null); waitFor(download, Succeeded); assertEquals(Succeeded, download.getState()); assertFalse(target.exists()); } @Test public void testDownloadWithCorrectChecksum() throws IOException { assertTrue(target.delete()); Download download = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, CONTENT_LENGTH, SHA1)), null); waitFor(download, Succeeded); assertEquals(Succeeded, download.getState()); String actual = readFileToString(target); assertEquals(EXPECTED, actual); } @Test public void testDownloadWithWrongContentLength() throws IOException { assertTrue(target.delete()); Download download = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, 4711L, SHA1)), null); waitFor(download, Downloading); waitFor(download, ChecksumError); assertEquals(ChecksumError, download.getState()); } @Test public void testDownloadWithWrongSHA1() throws IOException { assertTrue(target.delete()); Download download = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, CONTENT_LENGTH, "notdefined")), null); waitFor(download, Downloading); waitFor(download, ChecksumError); assertEquals(ChecksumError, download.getState()); } @Test public void testDownloadWithWrongLastModified() throws IOException { assertTrue(target.delete()); Download download = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, new Checksum(fromMillis(0L), CONTENT_LENGTH, SHA1)), null); waitFor(download, Downloading); // used to be an error but I haven't found the time yet to fix the timezone issues // waitFor(download, ChecksumError); waitFor(download, Succeeded); // assertEquals(ChecksumError, download.getState()); assertEquals(Succeeded, download.getState()); } @Test public void testDownloadWithWrongETag() throws IOException { assertTrue(target.delete()); Download download = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, CONTENT_LENGTH, SHA1)), null); waitFor(download, Downloading); waitFor(download, Succeeded); assertEquals(Succeeded, download.getState()); } @Test public void testResumeDownload() throws IOException { Download download = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, CONTENT_LENGTH, SHA1)), null); // write content to temp file and patch download object writeStringToFile(download.getTempFile(), LOREM_IPSUM_DOLOR_SIT_AMET); waitFor(download, Succeeded); assertEquals(Succeeded, download.getState()); String actual = readFileToString(target); assertEquals(EXPECTED, actual); } @Test public void testNotModifiedDownload() throws IOException { Download download = new Download("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, CONTENT_LENGTH, SHA1)), null); download.setETag(ETAG); Download queued = manager.queue(download, true); waitFor(queued, NotModified); } @Test public void testJustQueued() throws IOException { Download download = new Download("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, new Checksum(LAST_MODIFIED, CONTENT_LENGTH, SHA1)), null); download.setState(Succeeded); Download queued = manager.queue(download, false); assertEquals(download, queued); assertTrue(manager.getModel().getDownloads().contains(download)); } @Test public void testDownloadSameUrl() throws IOException { writeStringToFile(target, LOREM_IPSUM_DOLOR_SIT_AMET); Download download1 = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, null), null); Download download2 = manager.queueForDownload("447 Bytes", DOWNLOAD + "447bytes.txt", Copy, new FileAndChecksum(target, null), null); waitFor(download2, Succeeded); assertEquals(download2, download1); } @Test public void testDownloadWithHTTPS() throws IOException { writeStringToFile(target, LOREM_IPSUM_DOLOR_SIT_AMET); Download download = manager.queueForDownload("447 Bytes", DOWNLOAD.replaceAll("http", "https") + "447bytes.txt", Copy, new FileAndChecksum(target, null), null); waitFor(download, Succeeded); } @Test public void testDownloadAndFlatten() throws IOException { FileAndChecksum extracted = new FileAndChecksum(new File(target.getParentFile(), "447bytes.txt"), null); try { List<FileAndChecksum> fragments = singletonList(extracted); Download download = manager.queueForDownload("447 Bytes in a ZIP", DOWNLOAD + "447bytes.zip", Flatten, new FileAndChecksum(target.getParentFile(), null), fragments); waitFor(download, Processing); assertEquals(Processing, download.getState()); waitFor(download, Succeeded); assertEquals(Succeeded, download.getState()); assertTrue(extracted.getFile().exists()); String actual = readFileToString(extracted.getFile()); assertEquals(EXPECTED, actual); } finally { if (extracted.getFile().exists()) assertTrue(extracted.getFile().delete()); } } @Test public void testDownloadAndExtract() throws IOException { FileAndChecksum extracted = new FileAndChecksum(new File(target.getParentFile(), "first/second/447bytes.txt"), new Checksum(EXTRACTED_LAST_MODIFIED, CONTENT_LENGTH, SHA1)); try { List<FileAndChecksum> fragments = singletonList(extracted); Download download = manager.queueForDownload("447 Bytes in a ZIP", DOWNLOAD + "447bytes.zip", Extract, new FileAndChecksum(target.getParentFile(), new Checksum(ZIP_LAST_MODIFIED, ZIP_CONTENT_LENGTH, ZIP_SHA1)), fragments); waitFor(download, Processing); assertEquals(Processing, download.getState()); waitFor(download, Succeeded); assertEquals(Succeeded, download.getState()); assertTrue(extracted.getFile().exists()); String actual = readFileToString(extracted.getFile()); assertEquals(EXPECTED, actual); } finally { if (extracted.getFile().exists()) assertTrue(extracted.getFile().delete()); } } }