1 /* 2 * Copyright (C) 2011 The Guava Authors 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 com.google.common.base; 18 19 import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; 20 import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR; 21 import static com.google.common.truth.Truth.assertThat; 22 23 import com.google.common.annotations.GwtCompatible; 24 import com.google.common.annotations.GwtIncompatible; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.ImmutableSet; 27 import com.google.common.testing.GcFinalization; 28 import com.google.common.testing.NullPointerTester; 29 import com.google.common.testing.SerializableTester; 30 import java.io.File; 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.RetentionPolicy; 33 import java.lang.ref.WeakReference; 34 import java.lang.reflect.Field; 35 import java.net.MalformedURLException; 36 import java.net.URL; 37 import java.net.URLClassLoader; 38 import java.util.HashSet; 39 import java.util.Set; 40 import junit.framework.TestCase; 41 42 /** 43 * Tests for {@link Enums}. 44 * 45 * @author Steve McKay 46 */ 47 @GwtCompatible(emulated = true) 48 public class EnumsTest extends TestCase { 49 50 private enum TestEnum { 51 CHEETO, 52 HONDA, 53 POODLE, 54 } 55 56 private enum OtherEnum {} 57 testGetIfPresent()58 public void testGetIfPresent() { 59 assertThat(Enums.getIfPresent(TestEnum.class, "CHEETO")).hasValue(TestEnum.CHEETO); 60 assertThat(Enums.getIfPresent(TestEnum.class, "HONDA")).hasValue(TestEnum.HONDA); 61 assertThat(Enums.getIfPresent(TestEnum.class, "POODLE")).hasValue(TestEnum.POODLE); 62 63 assertThat(Enums.getIfPresent(TestEnum.class, "CHEETO")).isPresent(); 64 assertThat(Enums.getIfPresent(TestEnum.class, "HONDA")).isPresent(); 65 assertThat(Enums.getIfPresent(TestEnum.class, "POODLE")).isPresent(); 66 67 assertThat(Enums.getIfPresent(TestEnum.class, "CHEETO")).hasValue(TestEnum.CHEETO); 68 assertThat(Enums.getIfPresent(TestEnum.class, "HONDA")).hasValue(TestEnum.HONDA); 69 assertThat(Enums.getIfPresent(TestEnum.class, "POODLE")).hasValue(TestEnum.POODLE); 70 } 71 testGetIfPresent_caseSensitive()72 public void testGetIfPresent_caseSensitive() { 73 assertThat(Enums.getIfPresent(TestEnum.class, "cHEETO")).isAbsent(); 74 assertThat(Enums.getIfPresent(TestEnum.class, "Honda")).isAbsent(); 75 assertThat(Enums.getIfPresent(TestEnum.class, "poodlE")).isAbsent(); 76 } 77 testGetIfPresent_whenNoMatchingConstant()78 public void testGetIfPresent_whenNoMatchingConstant() { 79 assertThat(Enums.getIfPresent(TestEnum.class, "WOMBAT")).isAbsent(); 80 } 81 82 83 @GwtIncompatible // weak references testGetIfPresent_doesNotPreventClassUnloading()84 public void testGetIfPresent_doesNotPreventClassUnloading() throws Exception { 85 WeakReference<?> shadowLoaderReference = doTestClassUnloading(); 86 GcFinalization.awaitClear(shadowLoaderReference); 87 } 88 89 // Create a second ClassLoader and use it to get a second version of the TestEnum class. 90 // Run Enums.getIfPresent on that other TestEnum and then return a WeakReference containing the 91 // new ClassLoader. If Enums.getIfPresent does caching that prevents the shadow TestEnum 92 // (and therefore its ClassLoader) from being unloaded, then this WeakReference will never be 93 // cleared. 94 @GwtIncompatible // weak references doTestClassUnloading()95 private WeakReference<?> doTestClassUnloading() throws Exception { 96 URLClassLoader shadowLoader = new URLClassLoader(getClassPathUrls(), null); 97 @SuppressWarnings("unchecked") 98 Class<TestEnum> shadowTestEnum = 99 (Class<TestEnum>) Class.forName(TestEnum.class.getName(), false, shadowLoader); 100 assertNotSame(shadowTestEnum, TestEnum.class); 101 // We can't write Set<TestEnum> because that is a Set of the TestEnum from the original 102 // ClassLoader. 103 Set<Object> shadowConstants = new HashSet<>(); 104 for (TestEnum constant : TestEnum.values()) { 105 Optional<TestEnum> result = Enums.getIfPresent(shadowTestEnum, constant.name()); 106 assertThat(result).isPresent(); 107 shadowConstants.add(result.get()); 108 } 109 assertEquals(ImmutableSet.<Object>copyOf(shadowTestEnum.getEnumConstants()), shadowConstants); 110 Optional<TestEnum> result = Enums.getIfPresent(shadowTestEnum, "blibby"); 111 assertThat(result).isAbsent(); 112 return new WeakReference<>(shadowLoader); 113 } 114 testStringConverter_convert()115 public void testStringConverter_convert() { 116 Converter<String, TestEnum> converter = Enums.stringConverter(TestEnum.class); 117 assertEquals(TestEnum.CHEETO, converter.convert("CHEETO")); 118 assertEquals(TestEnum.HONDA, converter.convert("HONDA")); 119 assertEquals(TestEnum.POODLE, converter.convert("POODLE")); 120 assertNull(converter.convert(null)); 121 assertNull(converter.reverse().convert(null)); 122 } 123 testStringConverter_convertError()124 public void testStringConverter_convertError() { 125 Converter<String, TestEnum> converter = Enums.stringConverter(TestEnum.class); 126 try { 127 converter.convert("xxx"); 128 fail(); 129 } catch (IllegalArgumentException expected) { 130 } 131 } 132 testStringConverter_reverse()133 public void testStringConverter_reverse() { 134 Converter<String, TestEnum> converter = Enums.stringConverter(TestEnum.class); 135 assertEquals("CHEETO", converter.reverse().convert(TestEnum.CHEETO)); 136 assertEquals("HONDA", converter.reverse().convert(TestEnum.HONDA)); 137 assertEquals("POODLE", converter.reverse().convert(TestEnum.POODLE)); 138 } 139 140 @GwtIncompatible // NullPointerTester testStringConverter_nullPointerTester()141 public void testStringConverter_nullPointerTester() throws Exception { 142 Converter<String, TestEnum> converter = Enums.stringConverter(TestEnum.class); 143 NullPointerTester tester = new NullPointerTester(); 144 tester.testAllPublicInstanceMethods(converter); 145 } 146 testStringConverter_nullConversions()147 public void testStringConverter_nullConversions() { 148 Converter<String, TestEnum> converter = Enums.stringConverter(TestEnum.class); 149 assertNull(converter.convert(null)); 150 assertNull(converter.reverse().convert(null)); 151 } 152 153 @GwtIncompatible // Class.getName() testStringConverter_toString()154 public void testStringConverter_toString() { 155 assertEquals( 156 "Enums.stringConverter(com.google.common.base.EnumsTest$TestEnum.class)", 157 Enums.stringConverter(TestEnum.class).toString()); 158 } 159 testStringConverter_serialization()160 public void testStringConverter_serialization() { 161 SerializableTester.reserializeAndAssert(Enums.stringConverter(TestEnum.class)); 162 } 163 164 @GwtIncompatible // NullPointerTester testNullPointerExceptions()165 public void testNullPointerExceptions() { 166 NullPointerTester tester = new NullPointerTester(); 167 tester.testAllPublicStaticMethods(Enums.class); 168 } 169 170 @Retention(RetentionPolicy.RUNTIME) 171 private @interface ExampleAnnotation {} 172 173 private enum AnEnum { 174 @ExampleAnnotation 175 FOO, 176 BAR 177 } 178 179 @GwtIncompatible // reflection testGetField()180 public void testGetField() { 181 Field foo = Enums.getField(AnEnum.FOO); 182 assertEquals("FOO", foo.getName()); 183 assertTrue(foo.isAnnotationPresent(ExampleAnnotation.class)); 184 185 Field bar = Enums.getField(AnEnum.BAR); 186 assertEquals("BAR", bar.getName()); 187 assertFalse(bar.isAnnotationPresent(ExampleAnnotation.class)); 188 } 189 190 @GwtIncompatible // Class.getClassLoader() getClassPathUrls()191 private URL[] getClassPathUrls() { 192 ClassLoader classLoader = getClass().getClassLoader(); 193 return classLoader instanceof URLClassLoader 194 ? ((URLClassLoader) classLoader).getURLs() 195 : parseJavaClassPath().toArray(new URL[0]); 196 } 197 198 /** 199 * Returns the URLs in the class path specified by the {@code java.class.path} {@linkplain 200 * System#getProperty system property}. 201 */ 202 // TODO(b/65488446): Make this a public API. 203 @GwtIncompatible parseJavaClassPath()204 private static ImmutableList<URL> parseJavaClassPath() { 205 ImmutableList.Builder<URL> urls = ImmutableList.builder(); 206 for (String entry : Splitter.on(PATH_SEPARATOR.value()).split(JAVA_CLASS_PATH.value())) { 207 try { 208 try { 209 urls.add(new File(entry).toURI().toURL()); 210 } catch (SecurityException e) { // File.toURI checks to see if the file is a directory 211 urls.add(new URL("file", null, new File(entry).getAbsolutePath())); 212 } 213 } catch (MalformedURLException e) { 214 AssertionError error = new AssertionError("malformed class path entry: " + entry); 215 error.initCause(e); 216 throw error; 217 } 218 } 219 return urls.build(); 220 } 221 } 222