1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libcore.io; 18 19 import junit.framework.TestCase; 20 21 import java.io.File; 22 import java.io.FileNotFoundException; 23 import java.io.IOException; 24 import java.net.JarURLConnection; 25 import java.net.MalformedURLException; 26 import java.net.URL; 27 import java.net.URLConnection; 28 import java.net.URLStreamHandler; 29 import java.net.UnknownServiceException; 30 import java.util.Arrays; 31 32 import tests.support.resource.Support_Resources; 33 34 public class ClassPathURLStreamHandlerTest extends TestCase { 35 36 // A well formed jar file with 6 entries. 37 private static final String JAR = "ClassPathURLStreamHandlerTest.jar"; 38 private static final String ENTRY_IN_ROOT = "root.txt"; 39 private static final String DIR_ENTRY_WITHOUT_SLASH = "foo"; 40 private static final String DIR_ENTRY_WITH_SLASH = DIR_ENTRY_WITHOUT_SLASH + "/"; 41 private static final String ENTRY_IN_SUBDIR = "foo/bar/baz.txt"; 42 private static final String ENTRY_STORED = "stored_file.txt"; 43 private static final String ENTRY_WITH_SPACES_ENCODED = "file%20with%20spaces.txt"; 44 private static final String ENTRY_WITH_SPACES_UNENCODED = "file with spaces.txt"; 45 private static final String ENTRY_THAT_NEEDS_ESCAPING = "file_with_percent20_%20.txt"; 46 private static final String ENTRY_THAT_NEEDS_ESCAPING_ENCODED = "file_with_percent20_%2520.txt"; 47 private static final String ENTRY_WITH_RELATIVE_PATH = "foo/../foo/bar/baz.txt"; 48 private static final String MISSING_ENTRY = "Wrong.resource"; 49 50 private File jarFile; 51 52 @Override 53 protected void setUp() throws Exception { 54 File resources = Support_Resources.createTempFolder().getCanonicalFile(); 55 Support_Resources.copyFile(resources, null, JAR); 56 jarFile = new File(resources, JAR); 57 } 58 59 public void testConstructor() throws Exception { 60 try { 61 ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler("Missing.file"); 62 fail("Should throw IOException"); 63 } catch (IOException expected) { 64 } 65 66 String fileName = jarFile.getPath(); 67 ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler(fileName); 68 streamHandler.close(); 69 } 70 71 public void testGetEntryOrNull() throws Exception { 72 String fileName = jarFile.getPath(); 73 ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler(fileName); 74 75 checkGetEntryUrlOrNull(streamHandler, ENTRY_IN_ROOT, ENTRY_IN_ROOT); 76 checkGetEntryUrlOrNull(streamHandler, ENTRY_IN_SUBDIR, ENTRY_IN_SUBDIR); 77 checkGetEntryUrlOrNull(streamHandler, ENTRY_WITH_SPACES_UNENCODED, 78 ENTRY_WITH_SPACES_ENCODED); 79 checkGetEntryUrlOrNull(streamHandler, ENTRY_THAT_NEEDS_ESCAPING, 80 ENTRY_THAT_NEEDS_ESCAPING_ENCODED); 81 82 // getEntryOrNull() performs a lookup with and without trailing slash to handle directories. 83 // http://b/22527772 84 checkGetEntryUrlOrNull(streamHandler, DIR_ENTRY_WITHOUT_SLASH, 85 DIR_ENTRY_WITHOUT_SLASH); 86 checkGetEntryUrlOrNull(streamHandler, DIR_ENTRY_WITH_SLASH, DIR_ENTRY_WITH_SLASH); 87 88 assertNull(streamHandler.getEntryUrlOrNull(MISSING_ENTRY)); 89 assertNull(streamHandler.getEntryUrlOrNull("/" + ENTRY_IN_ROOT)); 90 assertNull(streamHandler.getEntryUrlOrNull("/" + ENTRY_IN_SUBDIR)); 91 assertNull(streamHandler.getEntryUrlOrNull(ENTRY_WITH_SPACES_ENCODED)); 92 assertNull(streamHandler.getEntryUrlOrNull(ENTRY_WITH_RELATIVE_PATH)); 93 assertNull(streamHandler.getEntryUrlOrNull("/" + DIR_ENTRY_WITHOUT_SLASH)); 94 assertNull(streamHandler.getEntryUrlOrNull("/" + DIR_ENTRY_WITH_SLASH)); 95 streamHandler.close(); 96 } 97 98 /** 99 * Check that the call to {@link ClassPathURLStreamHandler#getEntryUrlOrNull(String)} works as 100 * expected. 101 */ 102 private void checkGetEntryUrlOrNull(ClassPathURLStreamHandler streamHandler, 103 String entryName, String expectedJarRelativeURI) throws IOException { 104 105 String fileName = jarFile.getPath(); 106 URL urlOrNull = streamHandler.getEntryUrlOrNull(entryName); 107 assertNotNull("URL was unexpectedly null for " + entryName, urlOrNull); 108 assertEquals("jar:file:" + fileName + "!/" + expectedJarRelativeURI, 109 urlOrNull.toExternalForm()); 110 111 // Make sure that the resource could be opened and the correct contents returned, i.e. the 112 // same as those read from the jar file directly. 113 assertOpenConnectionOk(jarFile, expectedJarRelativeURI, streamHandler); 114 } 115 116 public void testIsEntryStored() throws IOException { 117 String fileName = jarFile.getPath(); 118 ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler(fileName); 119 120 assertFalse(streamHandler.isEntryStored("this/file/does/not/exist.txt")); 121 // This one is compressed 122 assertFalse(streamHandler.isEntryStored(ENTRY_IN_SUBDIR)); 123 assertTrue(streamHandler.isEntryStored(ENTRY_STORED)); 124 125 assertTrue(streamHandler.isEntryStored(DIR_ENTRY_WITHOUT_SLASH)); 126 127 // Directory entries are just stored, empty entries with "/" on the end of the name, so 128 // "true". 129 assertTrue(streamHandler.isEntryStored(DIR_ENTRY_WITH_SLASH)); 130 } 131 132 public void testOpenConnection() throws Exception { 133 String fileName = jarFile.getPath(); 134 ClassPathURLStreamHandler streamHandler = new ClassPathURLStreamHandler(fileName); 135 136 assertOpenConnectionOk(jarFile, ENTRY_IN_ROOT, streamHandler); 137 assertOpenConnectionOk(jarFile, ENTRY_IN_SUBDIR, streamHandler); 138 assertOpenConnectionOk(jarFile, ENTRY_WITH_SPACES_ENCODED, streamHandler); 139 assertOpenConnectionOk(jarFile, ENTRY_WITH_SPACES_UNENCODED, streamHandler); 140 assertOpenConnectionOk(jarFile, DIR_ENTRY_WITH_SLASH, streamHandler); 141 assertOpenConnectionOk(jarFile, DIR_ENTRY_WITHOUT_SLASH, streamHandler); 142 143 assertOpenConnectionConnectFails(jarFile, ENTRY_WITH_RELATIVE_PATH, streamHandler); 144 assertOpenConnectionConnectFails(jarFile, MISSING_ENTRY, streamHandler); 145 assertOpenConnectionConnectFails(jarFile, ENTRY_THAT_NEEDS_ESCAPING, streamHandler); 146 147 streamHandler.close(); 148 } 149 150 private void assertOpenConnectionConnectFails( 151 File jarFile, String entryName, URLStreamHandler streamHandler) throws IOException { 152 153 URL standardUrl = createJarUrl(jarFile, entryName, null /* use default stream handler */); 154 try { 155 standardUrl.openConnection().connect(); 156 fail(); 157 } catch (FileNotFoundException expected) { 158 } 159 160 URL actualUrl = createJarUrl(jarFile, entryName, streamHandler); 161 try { 162 actualUrl.openConnection().connect(); 163 fail(); 164 } catch (FileNotFoundException expected) { 165 } 166 } 167 168 private static void assertOpenConnectionOk(File jarFile, String entryName, 169 ClassPathURLStreamHandler streamHandler) throws IOException { 170 URL standardUrl = createJarUrl(jarFile, entryName, null /* use default stream handler */); 171 URLConnection standardUrlConnection = standardUrl.openConnection(); 172 assertNotNull(standardUrlConnection); 173 174 URL actualUrl = createJarUrl(jarFile, entryName, streamHandler); 175 URLConnection actualUrlConnection = actualUrl.openConnection(); 176 assertNotNull(actualUrlConnection); 177 assertBehaviorSame(standardUrlConnection, actualUrlConnection); 178 } 179 180 private static void assertBehaviorSame(URLConnection standardURLConnection, 181 URLConnection actualUrlConnection) throws IOException { 182 183 JarURLConnection standardJarUrlConnection = (JarURLConnection) standardURLConnection; 184 JarURLConnection actualJarUrlConnection = (JarURLConnection) actualUrlConnection; 185 186 byte[] actualBytes = Streams.readFully(actualJarUrlConnection.getInputStream()); 187 byte[] standardBytes = Streams.readFully(standardJarUrlConnection.getInputStream()); 188 assertEquals(Arrays.toString(standardBytes), Arrays.toString(actualBytes)); 189 190 try { 191 actualJarUrlConnection.getOutputStream(); 192 fail(); 193 } catch (UnknownServiceException expected) { 194 } 195 196 assertEquals( 197 standardJarUrlConnection.getJarFile().getName(), 198 actualJarUrlConnection.getJarFile().getName()); 199 200 assertEquals( 201 standardJarUrlConnection.getJarEntry().getName(), 202 actualJarUrlConnection.getJarEntry().getName()); 203 204 assertEquals( 205 standardJarUrlConnection.getJarFileURL(), 206 actualJarUrlConnection.getJarFileURL()); 207 208 assertEquals( 209 standardJarUrlConnection.getContentType(), 210 actualJarUrlConnection.getContentType()); 211 212 assertEquals( 213 standardJarUrlConnection.getContentLength(), 214 actualJarUrlConnection.getContentLength()); 215 } 216 217 private static URL createJarUrl(File jarFile, String entryName, URLStreamHandler streamHandler) 218 throws MalformedURLException { 219 return new URL("jar", null, -1, jarFile.toURI() + "!/" + entryName, streamHandler); 220 } 221 222 } 223