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