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