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