• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.content.type.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import libcore.content.type.MimeMap;
27 
28 import org.junit.Before;
29 import org.junit.Test;
30 
31 import java.net.FileNameMap;
32 import java.net.URLConnection;
33 import java.util.Locale;
34 import java.util.Objects;
35 import java.util.TreeMap;
36 
37 /**
38  * Tests {@link MimeMap#getDefault()}.
39  */
40 public class MimeMapTest {
41 
42     /** Stock Android's default MimeMap. */
43     private MimeMap stockAndroidMimeMap;
44 
45     /** Stock Android's default MimeMap. */
46     private MimeMap stockAndroidAltMimeMap;
47 
48     /** The platform's actual default MimeMap. */
49     private MimeMap mimeMap;
50 
setUp()51     @Before public void setUp() {
52         mimeMap = MimeMap.getDefault();
53         // A copy of stock Android's MimeMap.getDefault() from when this test was built,
54         // useful for comparing the platform's behavior vs. stock Android's.
55         // The resources are placed into the testres/ path by the "mimemap-testing-res.jar" genrule.
56         stockAndroidMimeMap = StockAndroidMimeMapFactory.create(
57                 s -> MimeMapTest.class.getResourceAsStream("/testres/" + s));
58         stockAndroidAltMimeMap =
59                 StockAndroidAltMimeMapFactory.create(
60                         s -> MimeMapTest.class.getResourceAsStream("/testres-alt/" + s));
61     }
62 
63     @Test
defaultMap_15715370()64     public void defaultMap_15715370() {
65         assertEquals("audio/flac", mimeMap.guessMimeTypeFromExtension("flac"));
66         assertEquals("flac", mimeMap.guessExtensionFromMimeType("audio/flac"));
67         assertEquals("flac", mimeMap.guessExtensionFromMimeType("application/x-flac"));
68     }
69 
70     // https://code.google.com/p/android/issues/detail?id=78909
bug78909()71     @Test public void bug78909() {
72         assertEquals("mka", mimeMap.guessExtensionFromMimeType("audio/x-matroska"));
73         assertEquals("mkv", mimeMap.guessExtensionFromMimeType("video/x-matroska"));
74     }
75 
bug16978217()76     @Test public void bug16978217() {
77         assertEquals("image/x-ms-bmp", mimeMap.guessMimeTypeFromExtension("bmp"));
78         assertEquals("image/x-icon", mimeMap.guessMimeTypeFromExtension("ico"));
79         assertEquals("video/mp2ts", mimeMap.guessMimeTypeFromExtension("ts"));
80     }
81 
testCommon()82     @Test public void testCommon() {
83         assertEquals("audio/mpeg", mimeMap.guessMimeTypeFromExtension("mp3"));
84         assertEquals("image/png", mimeMap.guessMimeTypeFromExtension("png"));
85         assertEquals("application/zip", mimeMap.guessMimeTypeFromExtension("zip"));
86 
87         assertEquals("mp3", mimeMap.guessExtensionFromMimeType("audio/mpeg"));
88         assertEquals("png", mimeMap.guessExtensionFromMimeType("image/png"));
89         assertEquals("zip", mimeMap.guessExtensionFromMimeType("application/zip"));
90     }
91 
bug18390752()92     @Test public void bug18390752() {
93         assertEquals("jpg", mimeMap.guessExtensionFromMimeType("image/jpeg"));
94     }
95 
bug30207891()96     @Test public void bug30207891() {
97         assertTrue(mimeMap.hasMimeType("IMAGE/PNG"));
98         assertTrue(mimeMap.hasMimeType("IMAGE/png"));
99         assertFalse(mimeMap.hasMimeType(""));
100         assertEquals("png", mimeMap.guessExtensionFromMimeType("IMAGE/PNG"));
101         assertEquals("png", mimeMap.guessExtensionFromMimeType("IMAGE/png"));
102         assertNull(mimeMap.guessMimeTypeFromExtension(""));
103         assertNull(mimeMap.guessMimeTypeFromExtension("doesnotexist"));
104         assertTrue(mimeMap.hasExtension("PNG"));
105         assertTrue(mimeMap.hasExtension("PnG"));
106         assertFalse(mimeMap.hasExtension(""));
107         assertFalse(mimeMap.hasExtension(".png"));
108         assertEquals("image/png", mimeMap.guessMimeTypeFromExtension("PNG"));
109         assertEquals("image/png", mimeMap.guessMimeTypeFromExtension("PnG"));
110         assertNull(mimeMap.guessMimeTypeFromExtension(".png"));
111         assertNull(mimeMap.guessMimeTypeFromExtension(""));
112         assertNull(mimeMap.guessExtensionFromMimeType("doesnotexist"));
113     }
114 
bug30793548()115     @Test public void bug30793548() {
116         assertEquals("video/3gpp", mimeMap.guessMimeTypeFromExtension("3gpp"));
117         assertEquals("video/3gpp", mimeMap.guessMimeTypeFromExtension("3gp"));
118         assertEquals("video/3gpp2", mimeMap.guessMimeTypeFromExtension("3gpp2"));
119         assertEquals("video/3gpp2", mimeMap.guessMimeTypeFromExtension("3g2"));
120     }
121 
bug37167977()122     @Test public void bug37167977() {
123         // https://tools.ietf.org/html/rfc5334#section-10.1
124         assertEquals("audio/ogg", mimeMap.guessMimeTypeFromExtension("ogg"));
125         assertEquals("audio/ogg", mimeMap.guessMimeTypeFromExtension("oga"));
126         assertEquals("audio/ogg", mimeMap.guessMimeTypeFromExtension("spx"));
127         assertEquals("video/ogg", mimeMap.guessMimeTypeFromExtension("ogv"));
128     }
129 
bug70851634_mimeTypeFromExtension()130     @Test public void bug70851634_mimeTypeFromExtension() {
131         assertEquals("video/vnd.youtube.yt", mimeMap.guessMimeTypeFromExtension("yt"));
132     }
133 
bug70851634_extensionFromMimeType()134     @Test public void bug70851634_extensionFromMimeType() {
135         assertEquals("yt", mimeMap.guessExtensionFromMimeType("video/vnd.youtube.yt"));
136         assertEquals("yt", mimeMap.guessExtensionFromMimeType("application/vnd.youtube.yt"));
137     }
138 
bug112162449_audio()139     @Test public void bug112162449_audio() {
140         // According to https://en.wikipedia.org/wiki/M3U#Internet_media_types
141         // this is a giant mess, so we pick "audio/x-mpegurl" because a similar
142         // playlist format uses "audio/x-scpls".
143         assertMimeTypeFromExtension("audio/x-mpegurl", "m3u");
144         assertMimeTypeFromExtension("audio/x-mpegurl", "m3u8");
145         assertExtensionFromMimeType("m3u", "audio/x-mpegurl");
146 
147         assertExtensionFromMimeType("m4a", "audio/mp4");
148         assertMimeTypeFromExtension("audio/mpeg", "m4a");
149 
150         assertBidirectional("audio/aac", "aac");
151     }
152 
bug112162449_video()153     @Test public void bug112162449_video() {
154         assertBidirectional("video/x-flv", "flv");
155         assertBidirectional("video/quicktime", "mov");
156         assertBidirectional("video/mpeg", "mpeg");
157     }
158 
bug112162449_image()159     @Test public void bug112162449_image() {
160         assertBidirectional("image/heif", "heif");
161         assertBidirectional("image/heif-sequence", "heifs");
162         assertBidirectional("image/heic", "heic");
163         assertBidirectional("image/heic-sequence", "heics");
164         assertMimeTypeFromExtension("image/heif", "hif");
165 
166         assertBidirectional("image/x-adobe-dng", "dng");
167         assertBidirectional("image/x-photoshop", "psd");
168 
169         assertBidirectional("image/jp2", "jp2");
170         assertMimeTypeFromExtension("image/jp2", "jpg2");
171     }
172 
bug141654151_image()173     @Test public void bug141654151_image() {
174         assertBidirectional("image/avif", "avif");
175     }
176 
bug120135571_audio()177     @Test public void bug120135571_audio() {
178         assertMimeTypeFromExtension("audio/mpeg", "m4r");
179     }
180 
bug154667531_consistent()181     @Test public void bug154667531_consistent() {
182         // Verify that if developers start from a strongly-typed MIME type, that
183         // sending it through a file extension doesn't lose that fidelity. We're
184         // only interested in the major MIME types that are relevant to
185         // permission models; we're not worried about generic types like
186         // "application/x-flac" being mapped to "audio/flac".
187         for (String before : mimeMap.mimeTypes()) {
188             final String beforeMajor = extractMajorMimeType(before);
189             switch (beforeMajor.toLowerCase(Locale.US)) {
190                 case "audio":
191                 case "video":
192                 case "image":
193                     final String extension = mimeMap.guessExtensionFromMimeType(before);
194                     final String after = mimeMap.guessMimeTypeFromExtension(extension);
195                     final String afterMajor = extractMajorMimeType(after);
196                     if (!beforeMajor.equalsIgnoreCase(afterMajor)) {
197                         fail("Expected " + before + " to map back to " + beforeMajor
198                                 + "/* after bouncing through file extension, "
199                                 + "but instead mapped to " + after);
200                     }
201                     break;
202             }
203         }
204     }
205 
wifiConfig_xml()206     @Test public void wifiConfig_xml() {
207         assertExtensionFromMimeType("xml", "application/x-wifi-config");
208         assertMimeTypeFromExtension("text/xml", "xml");
209     }
210 
x509CaCert()211     @Test public void x509CaCert() {
212         assertMimeTypeFromExtension("application/x-x509-ca-cert", "crt");
213         assertMimeTypeFromExtension("application/x-x509-ca-cert", "der");
214     }
215 
216     // http://b/183687627
webarchive_mht()217     @Test public void webarchive_mht() {
218         assertExtensionFromMimeType("mht", "multipart/related");
219         assertMimeTypeFromExtension("multipart/related", "mht");
220     }
221 
222     // http://b/122734564
nonLowercaseMimeType()223     @Test public void nonLowercaseMimeType() {
224         // A mixed-case mimeType that appears in mime.types; we expect guessMimeTypeFromExtension()
225         // to return it in lowercase because MimeMap considers lowercase to be the canonical form.
226         String mimeType = "application/vnd.ms-word.document.macroEnabled.12".toLowerCase(Locale.US);
227         assertBidirectional(mimeType, "docm");
228     }
229 
230     // Check that the keys given for lookups in either direction are not case sensitive
caseInsensitiveKeys()231     @Test public void caseInsensitiveKeys() {
232         String mimeType = mimeMap.guessMimeTypeFromExtension("apk");
233         assertNotNull(mimeType);
234 
235         assertEquals(mimeType, mimeMap.guessMimeTypeFromExtension("APK"));
236         assertEquals(mimeType, mimeMap.guessMimeTypeFromExtension("aPk"));
237 
238         assertEquals("apk", mimeMap.guessExtensionFromMimeType(mimeType));
239         assertEquals("apk", mimeMap.guessExtensionFromMimeType(
240                 mimeType.toUpperCase(Locale.US)));
241         assertEquals("apk", mimeMap.guessExtensionFromMimeType(
242                 mimeType.toLowerCase(Locale.US)));
243     }
244 
invalidKey_empty()245     @Test public void invalidKey_empty() {
246         checkInvalidExtension("");
247         checkInvalidMimeType("");
248     }
249 
invalidKey_null()250     @Test public void invalidKey_null() {
251         checkInvalidExtension(null);
252         checkInvalidMimeType(null);
253     }
254 
invalidKey()255     @Test public void invalidKey() {
256         checkInvalidMimeType("invalid mime type");
257         checkInvalidExtension("invalid extension");
258     }
259 
containsAllStockAndroidMappings_mimeToExt()260     @Test public void containsAllStockAndroidMappings_mimeToExt() {
261         assertTrue(
262                 mimeToExtAsExpected(stockAndroidMimeMap, mimeMap)
263                         || mimeToExtAsExpected(stockAndroidAltMimeMap, mimeMap));
264     }
265 
mimeToExtAsExpected(MimeMap stockMimeMap, MimeMap actualMimeMap)266     private static boolean mimeToExtAsExpected(MimeMap stockMimeMap, MimeMap actualMimeMap) {
267         // The minimum expected mimeType -> extension mappings that should be present.
268         TreeMap<String, String> expected = new TreeMap<>();
269         // The extensions that these mimeTypes are actually mapped to.
270         TreeMap<String, String> actual = new TreeMap<>();
271         for (String mimeType : stockMimeMap.mimeTypes()) {
272             expected.put(mimeType, stockMimeMap.guessExtensionFromMimeType(mimeType));
273             actual.put(mimeType, actualMimeMap.guessExtensionFromMimeType(mimeType));
274         }
275         return expected.equals(actual);
276     }
277 
containsAllExpectedMappings_extToMime()278     @Test public void containsAllExpectedMappings_extToMime() {
279         assertTrue(
280                 extToMimeAsExpected(stockAndroidMimeMap, mimeMap)
281                         || extToMimeAsExpected(stockAndroidAltMimeMap, mimeMap));
282     }
283 
extToMimeAsExpected(MimeMap stockMimeMap, MimeMap actualMimeMap)284     private static boolean extToMimeAsExpected(MimeMap stockMimeMap, MimeMap actualMimeMap) {
285         // The minimum expected extension -> mimeType mappings that should be present.
286         TreeMap<String, String> expected = new TreeMap<>();
287         // The mimeTypes that these extensions are actually mapped to.
288         TreeMap<String, String> actual = new TreeMap<>();
289         for (String extension : stockMimeMap.extensions()) {
290             expected.put(extension, stockMimeMap.guessMimeTypeFromExtension(extension));
291             actual.put(extension, actualMimeMap.guessMimeTypeFromExtension(extension));
292         }
293         return expected.equals(actual);
294     }
295 
296     /**
297      * Checks that MimeTypeMap and URLConnection.getFileNameMap()'s behavior is
298      * consistent with MimeMap.getDefault(), i.e. that they are implemented on
299      * top of MimeMap.getDefault().
300      */
agreesWithPublicApi()301     @Test public void agreesWithPublicApi() {
302         android.webkit.MimeTypeMap webkitMap = android.webkit.MimeTypeMap.getSingleton();
303         FileNameMap urlConnectionMap = URLConnection.getFileNameMap();
304 
305         for (String extension : mimeMap.extensions()) {
306             String mimeType = mimeMap.guessMimeTypeFromExtension(extension);
307             Objects.requireNonNull(mimeType);
308             assertEquals(mimeType, webkitMap.getMimeTypeFromExtension(extension));
309             assertTrue(webkitMap.hasExtension(extension));
310             assertEquals(mimeType, urlConnectionMap.getContentTypeFor("filename." + extension));
311         }
312 
313         for (String mimeType : mimeMap.mimeTypes()) {
314             String extension = mimeMap.guessExtensionFromMimeType(mimeType);
315             Objects.requireNonNull(extension);
316             assertEquals(extension, webkitMap.getExtensionFromMimeType(mimeType));
317             assertTrue(webkitMap.hasMimeType(mimeType));
318         }
319     }
320 
bug179151942_jpeg_xl()321     @Test public void bug179151942_jpeg_xl() {
322         assertBidirectional("image/jxl", "jxl");
323     }
324 
checkInvalidExtension(String s)325     private void checkInvalidExtension(String s) {
326         assertFalse(mimeMap.hasExtension(s));
327         assertNull(mimeMap.guessMimeTypeFromExtension(s));
328     }
329 
checkInvalidMimeType(String s)330     private void checkInvalidMimeType(String s) {
331         assertFalse(mimeMap.hasMimeType(s));
332         assertNull(mimeMap.guessExtensionFromMimeType(s));
333     }
334 
assertMimeTypeFromExtension(String mimeType, String extension)335     private void assertMimeTypeFromExtension(String mimeType, String extension) {
336         final String actual = mimeMap.guessMimeTypeFromExtension(extension);
337         if (!Objects.equals(mimeType, actual)) {
338             fail("Expected " + mimeType + " but was " + actual + " for extension " + extension);
339         }
340     }
341 
assertExtensionFromMimeType(String extension, String mimeType)342     private void assertExtensionFromMimeType(String extension, String mimeType) {
343         final String actual = mimeMap.guessExtensionFromMimeType(mimeType);
344         if (!Objects.equals(extension, actual)) {
345             fail("Expected " + extension + " but was " + actual + " for type " + mimeType);
346         }
347     }
348 
assertBidirectional(String mimeType, String extension)349     private void assertBidirectional(String mimeType, String extension) {
350         assertMimeTypeFromExtension(mimeType, extension);
351         assertExtensionFromMimeType(extension, mimeType);
352     }
353 
extractMajorMimeType(String mimeType)354     private static String extractMajorMimeType(String mimeType) {
355         final int slash = mimeType.indexOf('/');
356         return (slash != -1) ? mimeType.substring(0, slash) : mimeType;
357     }
358 }
359