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