• 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 libcore.libcore.content.type;
18 
19 import org.junit.After;
20 import org.junit.Before;
21 import org.junit.Test;
22 
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.Set;
30 import libcore.content.type.MimeMap;
31 
32 import static org.junit.Assert.assertEquals;
33 import static org.junit.Assert.assertFalse;
34 import static org.junit.Assert.assertNotSame;
35 import static org.junit.Assert.assertNull;
36 import static org.junit.Assert.assertSame;
37 import static org.junit.Assert.assertTrue;
38 import static org.junit.Assert.fail;
39 
40 /**
41  * Tests {@link MimeMap} and {@link MimeMap.Builder}.
42  */
43 public class MimeMapTest {
44 
45     private MimeMap mimeMap;
46     private MimeMap emptyMap;
47 
setUp()48     @Before public void setUp() {
49         mimeMap = MimeMap.getDefault();
50         emptyMap = MimeMap.builder().build();
51     }
52 
tearDown()53     @After public void tearDown() {
54         mimeMap = null;
55     }
56 
lookup_invalidExtension()57     @Test public void lookup_invalidExtension() {
58         assertNull(mimeMap.guessMimeTypeFromExtension(null));
59         assertNull(mimeMap.guessMimeTypeFromExtension(""));
60         assertFalse(mimeMap.hasExtension(null));
61         assertFalse(mimeMap.hasExtension(""));
62     }
63 
lookup_invalidMimeType()64     @Test public void lookup_invalidMimeType() {
65         assertNull(mimeMap.guessExtensionFromMimeType(null));
66         assertNull(mimeMap.guessExtensionFromMimeType(""));
67         assertFalse(mimeMap.hasMimeType(null));
68         assertFalse(mimeMap.hasMimeType(""));
69     }
70 
caseNormalization_key()71     @Test public void caseNormalization_key() {
72         mimeMap = MimeMap.builder()
73                 .addMimeMapping("application/msWord", Arrays.asList("Doc"))
74                 .build();
75         assertEquals("application/msword", mimeMap.guessMimeTypeFromExtension("dOc"));
76         assertEquals("doc", mimeMap.guessExtensionFromMimeType("appliCATion/mSWOrd"));
77         assertTrue(mimeMap.hasMimeType("application/msword"));
78         assertTrue(mimeMap.hasMimeType("Application/mSWord"));
79 
80         assertTrue(mimeMap.hasExtension("doc"));
81         assertTrue(mimeMap.hasExtension("DOC"));
82         assertTrue(mimeMap.hasExtension("dOc"));
83     }
84 
caseNormalization_value()85     @Test public void caseNormalization_value() {
86         // Default map
87         for (String extension : mimeMap.extensions()) {
88             assertLowerCase(mimeMap.guessMimeTypeFromExtension(extension));
89         }
90         for (String mimeType : mimeMap.mimeTypes()) {
91             assertLowerCase(mimeMap.guessExtensionFromMimeType(mimeType));
92         }
93 
94         // Known keys for a custom map
95         mimeMap = MimeMap.builder()
96                 .addMimeMapping("application/msWord", Arrays.asList("Doc"))
97                 .build();
98         assertEquals("doc", mimeMap.guessExtensionFromMimeType("Application/mSWord"));
99         assertEquals("application/msword", mimeMap.guessMimeTypeFromExtension("DoC"));
100     }
101 
assertLowerCase(String s)102     private static void assertLowerCase(String s) {
103         assertEquals(s.toLowerCase(Locale.ROOT), s);
104     }
105 
unmapped()106     @Test public void unmapped() {
107         mimeMap = MimeMap.builder()
108                 .addMimeMapping("mime/test", Arrays.asList("test", "tst"))
109                 .build();
110         assertNull(mimeMap.guessExtensionFromMimeType("mime/unknown"));
111         assertFalse(mimeMap.hasMimeType("mime/unknown"));
112 
113         assertNull(mimeMap.guessMimeTypeFromExtension("absent"));
114         assertFalse(mimeMap.hasExtension("absent"));
115     }
116 
getDefault_returnsSameInstance()117     @Test public void getDefault_returnsSameInstance() {
118         assertSame(MimeMap.getDefault(), MimeMap.getDefault());
119     }
120 
getDefault_afterSetDefaultSupplier()121     @Test public void getDefault_afterSetDefaultSupplier() {
122         MimeMap originalDefault = MimeMap.getDefault();
123         try {
124             // Constructs a new instance every time it is called
125             MimeMap.setDefaultSupplier(() -> MimeMap.builder().addMimeMapping("mime/sup", "sup").build());
126             // Same instance is returned both times
127             assertSame(MimeMap.getDefault(), MimeMap.getDefault());
128             // Check that the supplier is in effect
129             assertTrue(originalDefault != MimeMap.getDefault());
130             assertEquals("mime/sup", MimeMap.getDefault().guessMimeTypeFromExtension("sup"));
131         } finally {
132             MimeMap.setDefaultSupplier(() -> originalDefault);
133         }
134         assertSame(originalDefault, MimeMap.getDefault());
135     }
136 
setDefaultSupplier_returningNull()137     @Test public void setDefaultSupplier_returningNull() {
138         MimeMap originalDefault = MimeMap.getDefault();
139         try {
140             // A Supplier that returns null is invalid, but we only notice during getDefault().
141             MimeMap.setDefaultSupplier(() -> null);
142             try {
143                 MimeMap.getDefault();
144                 fail();
145             } catch (NullPointerException expected) {
146             }
147         } finally {
148             MimeMap.setDefaultSupplier(() -> originalDefault);
149         }
150     }
151 
buildUpon()152     @Test public void buildUpon() {
153         mimeMap = MimeMap.builder()
154                 .build();
155         assertMap(
156                 makeMap(),
157                 makeMap(),
158                 mimeMap);
159 
160         mimeMap = mimeMap.buildUpon()
161                 .build();
162         assertMap(
163                 makeMap(),
164                 makeMap(),
165                 mimeMap);
166 
167         mimeMap = mimeMap.buildUpon()
168                 .addMimeMapping("text/plain", "txt")
169                 .build();
170         assertMap(
171                 makeMap("text/plain", "txt"),
172                 makeMap("txt", "text/plain"),
173                 mimeMap);
174 
175         mimeMap = mimeMap.buildUpon()
176                 .addMimeMapping("audio/mpeg", Arrays.asList("mp2", "mp3"))
177                 .build();
178         assertMap(
179                 makeMap("audio/mpeg", "mp2",
180                         "text/plain", "txt"),
181                 makeMap("mp2", "audio/mpeg",
182                         "mp3", "audio/mpeg",
183                         "txt", "text/plain"),
184                 mimeMap);
185 
186         mimeMap = mimeMap.buildUpon()
187                 .addMimeMapping("text/plain", "text")
188                 .build();
189         assertMap(
190                 makeMap("audio/mpeg", "mp2",
191                         "text/plain", "text"),
192                 makeMap("mp2", "audio/mpeg",
193                         "mp3", "audio/mpeg",
194                         "text", "text/plain",
195                         "txt", "text/plain"),
196                 mimeMap);
197     }
198 
put()199     @Test public void put() {
200         MimeMap a = MimeMap.builder()
201                 .addMimeMapping("text/plain", Arrays.asList("txt", "text"))
202                 .addMimeMapping("application/msword", "doc")
203                 .build();
204         MimeMap b = MimeMap.builder()
205                 .addMimeMapping("text/plain", Arrays.asList("txt", "text"))
206                 .addMimeMapping("application/msword", "doc")
207                 .build();
208         assertEqualsButNotSame(a, b);
209         assertEqualsButNotSame(a, a.buildUpon().build());
210         assertMap(
211                 makeMap(
212                         "text/plain", "txt",
213                         "application/msword", "doc"),
214                 makeMap("txt", "text/plain",
215                         "text", "text/plain",
216                         "doc", "application/msword"),
217                 a);
218     }
219 
put_noExtensions()220     @Test public void put_noExtensions() {
221         checkPut_noExtensions(emptyMap);
222         checkPut_noExtensions(MimeMap.builder().addMimeMapping("text/plain", "txt").build());
223         checkPut_noExtensions(mimeMap);
224     }
225 
226     /**
227      * Checks that put(String, emptyList()) doesn't change or add any mappings.
228      */
checkPut_noExtensions(MimeMap baseMap)229     private static void checkPut_noExtensions(MimeMap baseMap) {
230         MimeMap mimeMap = baseMap.buildUpon()
231                 .addMimeMapping("mime/type", Collections.emptyList())
232                 .build();
233         assertEquals(baseMap, mimeMap);
234     }
235 
put_String_List_nullOrEmpty()236     @Test public void put_String_List_nullOrEmpty() {
237         // We still check mimeType for validity even if no extensions are specified
238         assertPutThrowsNpe(null);
239         assertPutThrowsIae("");
240 
241         // null or "" are not allowed for either MIME type or extension
242         assertPutThrowsNpe(null, "ext");
243         assertPutThrowsIae("", "ext");
244         assertPutThrowsNpe("mime/type", (String) null);
245         assertPutThrowsIae("mime/type", "");
246 
247         assertPutThrowsNpe("mime/type", "ext", null);
248         assertPutThrowsIae("mime/type", "ext", "");
249     }
250 
put_String_String_nullOrEmpty()251     @Test public void put_String_String_nullOrEmpty() {
252         assertThrowsNpe(() -> MimeMap.builder().addMimeMapping(null, "ext"));
253         assertThrowsIae(() -> MimeMap.builder().addMimeMapping("", "ext"));
254 
255         assertThrowsNpe(() -> MimeMap.builder().addMimeMapping("mime/type", (String) null));
256         assertThrowsIae(() -> MimeMap.builder().addMimeMapping("mime/type", ""));
257     }
258 
259     /**
260      * Tests put() arguments that have a prefix {@code "?"} which leads to putIfAbsent semantics.
261      */
putIfAbsent()262     @Test public void putIfAbsent() {
263         // Starting from an empty mapping, add a bunch more, some with and some without '?'.
264         mimeMap = MimeMap.builder()
265                 .addMimeMapping("?text/plain", "?txt")
266                 .addMimeMapping("audio/mpeg", Arrays.asList("mpga", "mpega", "?mp2", "mp3"))
267                 .build();
268         assertEquals("txt", mimeMap.guessExtensionFromMimeType("text/plain"));
269         assertEquals("text/plain", mimeMap.guessMimeTypeFromExtension("txt"));
270         assertEquals("mpga", mimeMap.guessExtensionFromMimeType("audio/mpeg"));
271         assertEquals("audio/mpeg", mimeMap.guessMimeTypeFromExtension("mp2"));
272         assertEquals("audio/mpeg", mimeMap.guessMimeTypeFromExtension("mp3"));
273 
274         // Override a ext -> MIME mapping without overriding the MIME -> ext mapping.
275         mimeMap = mimeMap.buildUpon()
276                 .addMimeMapping("?audio/mpeg", "m4a")
277                 .build();
278         assertEquals("mpga", mimeMap.guessExtensionFromMimeType("audio/mpeg"));
279         assertEquals("audio/mpeg", mimeMap.guessMimeTypeFromExtension("m4a"));
280 
281         // Override a MIME -> ext mapping without overriding the ext -> MIME mapping.
282         mimeMap = mimeMap.buildUpon()
283                 .addMimeMapping("audio/mpeg", "?txt")
284                 .build();
285         assertEquals("txt", mimeMap.guessExtensionFromMimeType("audio/mpeg"));
286         assertEquals("text/plain", mimeMap.guessMimeTypeFromExtension("txt"));
287 
288 
289         // Check final state
290         assertMap(
291                 makeMap(
292                         "text/plain", "txt",
293                         "audio/mpeg", "txt"
294                         ),
295                 makeMap(
296                         "txt", "text/plain",
297                         "m4a", "audio/mpeg",
298                         "mp2", "audio/mpeg",
299                         "mp3", "audio/mpeg",
300                         "mpega", "audio/mpeg",
301                         "mpga", "audio/mpeg"
302                 ),
303                 mimeMap
304         );
305     }
306 
extensions()307     @Test public void extensions() {
308         assertEquals(Collections.emptySet(), emptyMap.extensions());
309         mimeMap = MimeMap.builder()
310                 .addMimeMapping("text/plain", Arrays.asList("txt", "text"))
311                 .addMimeMapping("audi/mpeg", "m4a")
312                 .addMimeMapping("application/msword", "doc")
313                 .addMimeMapping("text/plain", "tx")
314                 .build();
315         Set<String> extensions = new HashSet<>(Arrays.asList(
316                 "txt", "text", "m4a", "doc", "tx"));
317         assertEquals(extensions, mimeMap.extensions());
318         // Check that the extensions() view is unmodifiable
319         try {
320             mimeMap.extensions().add("ext");
321             fail();
322         } catch (UnsupportedOperationException expected) {
323         }
324     }
325 
mimeTypes()326     @Test public void mimeTypes() {
327         assertEquals(Collections.emptySet(), emptyMap.mimeTypes());
328         mimeMap = MimeMap.builder()
329                 .addMimeMapping("text/plain", Arrays.asList("txt", "text"))
330                 .addMimeMapping("audio/mpeg", "m4a")
331                 .addMimeMapping("application/msword", "doc")
332                 .addMimeMapping("text/plain", "tx")
333                 .build();
334         Set<String> mimeTypes = new HashSet<>(Arrays.asList(
335                 "text/plain",
336                 "audio/mpeg",
337                 "application/msword"));
338         assertEquals(mimeTypes, mimeMap.mimeTypes());
339         // Check that the mimeTypes() view is unmodifiable
340         try {
341             mimeMap.mimeTypes().add("foo/bar");
342             fail();
343         } catch (UnsupportedOperationException expected) {
344         }
345     }
346 
347     /**
348      * Tests invalid put() invocations that have '?' in additional/invalid places.
349      */
put_invalid_additionalQuestionMarks()350     @Test public void put_invalid_additionalQuestionMarks() {
351         // Potentially we could tolerate additional ? as a prefix in future, but right now we don't.
352         assertPutThrowsIae("??text/plain", "txt");
353         assertPutThrowsIae("text/p?lain", "txt");
354         assertPutThrowsIae("text/plain", "txt", "t?ext");
355         assertPutThrowsIae("text/plain", "??txt");
356         assertPutThrowsIae("text/plain", "t?xt");
357     }
358 
359     /** Checks that MIME types must have a '/', while extensions must not. */
put_invalid_slash()360     @Test public void put_invalid_slash() {
361         assertPutThrowsIae("mime/type", "invalid/ext");
362         assertPutThrowsIae("invalidmime", "ext");
363 
364         // During lookups, wrong arguments return null rather than throwing.
365         mimeMap = MimeMap.builder().addMimeMapping("mime/type", "ext").build();
366         assertNull(mimeMap.guessExtensionFromMimeType("ext")); // ext is no mime type
367         assertNull(mimeMap.guessMimeTypeFromExtension("mime/type")); // mime/type is no extension
368     }
369 
assertPutThrowsNpe(String mime, String... exts)370     private static void assertPutThrowsNpe(String mime, String... exts) {
371         assertThrowsNpe(() -> MimeMap.builder().addMimeMapping(mime, Arrays.asList(exts)));
372     }
373 
assertPutThrowsIae(final String mime, final String... exts)374     private static void assertPutThrowsIae(final String mime, final String... exts) {
375         assertThrowsIae(() -> MimeMap.builder().addMimeMapping(mime, Arrays.asList(exts)));
376     }
377 
assertThrowsNpe(Runnable runnable)378     private static void assertThrowsNpe(Runnable runnable) {
379         try {
380             runnable.run();
381             fail();
382         } catch (NullPointerException expected) {
383         }
384     }
385 
assertThrowsIae(Runnable runnable)386     private static void assertThrowsIae(Runnable runnable) {
387         try {
388             runnable.run();
389             fail();
390         } catch (IllegalArgumentException expected) {
391         }
392     }
393 
hashCodeValue()394     @Test public void hashCodeValue() {
395         assertEquals(0, emptyMap.hashCode());
396         MimeMap a = MimeMap.builder().addMimeMapping("mime/test", "test").build();
397         MimeMap b = a.buildUpon().addMimeMapping("foo/bar", "baz").build();
398         assertTrue(0 != a.hashCode());
399         assertTrue((a.hashCode() != b.hashCode()));
400     }
401 
empty_copies()402     @Test public void empty_copies() {
403         assertEqualsButNotSame(emptyMap, MimeMap.builder().build());
404         assertEqualsButNotSame(emptyMap, emptyMap.buildUpon().build());
405     }
406 
407     /** Creates a map from alternating key/value arguments, useful for test assertions. */
makeMap(String... keysAndValues)408     private static Map<String, String> makeMap(String... keysAndValues) {
409         if (keysAndValues.length % 2 != 0) {
410             throw new IllegalArgumentException(
411                     "Invalid length " + keysAndValues.length + ": " + keysAndValues);
412         }
413         Map<String, String> result = new HashMap<>();
414         for (int i = 0; i < keysAndValues.length; i += 2) {
415             String key = keysAndValues[i];
416             String value = keysAndValues[i + 1];
417             result.put(key, value);
418         }
419         return result;
420 
421     }
422 
423     /**
424      * Asserts that the given {@code MimeMap} has exactly the given mime -> ext and ext -> mime
425      * mappings, but no others.
426      */
assertMap( Map<String, String> expectedMimeToExt, Map<String, String> expectedExtToMime, MimeMap mimeMap)427     private static<T> void assertMap(
428             Map<String, String> expectedMimeToExt,
429             Map<String, String> expectedExtToMime,
430             MimeMap mimeMap)
431     {
432         MimeMap.Builder expectedBuilder = MimeMap.builder();
433         for (Map.Entry<String, String> entry : expectedExtToMime.entrySet()) {
434             String ext = entry.getKey();
435             String mime = entry.getValue();
436             assertEquals(ext + ": " + mimeMap, mime, mimeMap.guessMimeTypeFromExtension(ext));
437             expectedBuilder.addMimeMapping("?" + mime, ext);
438         }
439         for (Map.Entry<String, String> entry : expectedMimeToExt.entrySet()) {
440             String mime = entry.getKey();
441             String ext = entry.getValue();
442             assertEquals(mime + ": "  + mimeMap, ext, mimeMap.guessExtensionFromMimeType(mime));
443             expectedBuilder.addMimeMapping(mime, "?" + ext);
444         }
445         // Check that there are no unexpected additional mappings.
446         assertEqualsButNotSame(expectedBuilder.build(), mimeMap);
447     }
448 
assertEqualsButNotSame(T a, T b)449     private static<T> void assertEqualsButNotSame(T a, T b) {
450         assertEquals(a, b);
451         assertEquals(b, a);
452         assertNotSame(a, b);
453         assertEquals(a.hashCode(), b.hashCode());
454     }
455 
456 }
457