• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import org.junit.Ignore;
34 import protobuf_unittest.NonNestedExtension;
35 import protobuf_unittest.NonNestedExtensionLite;
36 import java.lang.reflect.Method;
37 import java.net.URLClassLoader;
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.HashSet;
41 import java.util.Set;
42 import junit.framework.Test;
43 import junit.framework.TestCase;
44 import junit.framework.TestSuite;
45 
46 /**
47  * Tests for {@link ExtensionRegistryFactory} and the {@link ExtensionRegistry} instances it
48  * creates.
49  *
50  * <p>This test simulates the runtime behaviour of the ExtensionRegistryFactory by delegating test
51  * definitions to two inner classes {@link InnerTest} and {@link InnerLiteTest}, the latter of which
52  * is executed using a custom ClassLoader, simulating the ProtoLite environment.
53  *
54  * <p>The test mechanism employed here is based on the pattern in {@code
55  * com.google.common.util.concurrent.AbstractFutureFallbackAtomicHelperTest}
56  *
57  * <p> This test is temporarily disabled due to what appears to be a subtle change to class loading
58  * behavior in Java 11. That seems to have broken the way the test uses a custom ClassLoader to
59  * exercise Lite functionality.
60  */
61 @Ignore
62 public class ExtensionRegistryFactoryTest extends TestCase {
63 
64   // A classloader which blacklists some non-Lite classes.
65   private static final ClassLoader LITE_CLASS_LOADER = getLiteOnlyClassLoader();
66 
67   /** Defines the set of test methods which will be run. */
68   static interface RegistryTests {
testCreate()69     void testCreate();
70 
testEmpty()71     void testEmpty();
72 
testIsFullRegistry()73     void testIsFullRegistry();
74 
testAdd()75     void testAdd();
76 
testAdd_immutable()77     void testAdd_immutable();
78   }
79 
80   /** Test implementations for the non-Lite usage of ExtensionRegistryFactory. */
81   public static class InnerTest implements RegistryTests {
82 
83     @Override
testCreate()84     public void testCreate() {
85       ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
86 
87       assertEquals(registry.getClass(), ExtensionRegistry.class);
88     }
89 
90     @Override
testEmpty()91     public void testEmpty() {
92       ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty();
93 
94       assertEquals(emptyRegistry.getClass(), ExtensionRegistry.class);
95       assertEquals(emptyRegistry, ExtensionRegistry.EMPTY_REGISTRY);
96     }
97 
98     @Override
testIsFullRegistry()99     public void testIsFullRegistry() {
100       ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
101       assertTrue(ExtensionRegistryFactory.isFullRegistry(registry));
102     }
103 
104     @Override
testAdd()105     public void testAdd() {
106       ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance();
107       NonNestedExtensionLite.registerAllExtensions(registry1);
108       registry1.add(NonNestedExtensionLite.nonNestedExtensionLite);
109 
110       ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance();
111       NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2);
112       registry2.add(NonNestedExtension.nonNestedExtension);
113 
114       ExtensionRegistry fullRegistry1 = (ExtensionRegistry) registry1;
115       ExtensionRegistry fullRegistry2 = (ExtensionRegistry) registry2;
116 
117       assertTrue(
118           "Test is using a non-lite extension",
119           GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(
120               NonNestedExtensionLite.nonNestedExtensionLite.getClass()));
121       assertNull(
122           "Extension is not registered in masqueraded full registry",
123           fullRegistry1.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension"));
124       GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?>
125           extension =
126               registry1.findLiteExtensionByNumber(
127                   NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1);
128       assertNotNull("Extension registered in lite registry", extension);
129 
130       assertTrue(
131           "Test is using a non-lite extension",
132           Extension.class.isAssignableFrom(NonNestedExtension.nonNestedExtension.getClass()));
133       assertNotNull(
134           "Extension is registered in masqueraded full registry",
135           fullRegistry2.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension"));
136     }
137 
138     @Override
testAdd_immutable()139     public void testAdd_immutable() {
140       ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance().getUnmodifiable();
141       try {
142         NonNestedExtensionLite.registerAllExtensions(registry1);
143         fail();
144       } catch (UnsupportedOperationException expected) {
145       }
146       try {
147         registry1.add(NonNestedExtensionLite.nonNestedExtensionLite);
148         fail();
149       } catch (UnsupportedOperationException expected) {
150       }
151 
152       ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance().getUnmodifiable();
153       try {
154         NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2);
155         fail();
156       } catch (IllegalArgumentException expected) {
157       }
158       try {
159         registry2.add(NonNestedExtension.nonNestedExtension);
160         fail();
161       } catch (IllegalArgumentException expected) {
162       }
163     }
164   }
165 
166   /** Test implementations for the Lite usage of ExtensionRegistryFactory. */
167   public static final class InnerLiteTest implements RegistryTests {
168 
169     @Override
testCreate()170     public void testCreate() {
171       ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
172 
173       assertEquals(registry.getClass(), ExtensionRegistryLite.class);
174     }
175 
176     @Override
testEmpty()177     public void testEmpty() {
178       ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty();
179 
180       assertEquals(emptyRegistry.getClass(), ExtensionRegistryLite.class);
181       assertEquals(emptyRegistry, ExtensionRegistryLite.EMPTY_REGISTRY_LITE);
182     }
183 
184     @Override
testIsFullRegistry()185     public void testIsFullRegistry() {
186       ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
187       assertFalse(ExtensionRegistryFactory.isFullRegistry(registry));
188     }
189 
190     @Override
testAdd()191     public void testAdd() {
192       ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
193       NonNestedExtensionLite.registerAllExtensions(registry);
194       GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?>
195           extension =
196               registry.findLiteExtensionByNumber(
197                   NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1);
198       assertNotNull("Extension is registered in Lite registry", extension);
199     }
200 
201     @Override
testAdd_immutable()202     public void testAdd_immutable() {
203       ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance().getUnmodifiable();
204       try {
205         NonNestedExtensionLite.registerAllExtensions(registry);
206         fail();
207       } catch (UnsupportedOperationException expected) {
208       }
209     }
210   }
211 
212   /** Defines a suite of tests which the JUnit3 runner retrieves by reflection. */
suite()213   public static Test suite() {
214     TestSuite suite = new TestSuite();
215     for (Method method : RegistryTests.class.getMethods()) {
216       suite.addTest(TestSuite.createTest(ExtensionRegistryFactoryTest.class, method.getName()));
217     }
218     return suite;
219   }
220 
221   /**
222    * Sequentially runs first the Lite and then the non-Lite test variant via classloader
223    * manipulation.
224    */
225   @Override
runTest()226   public void runTest() throws Exception {
227     ClassLoader storedClassLoader = Thread.currentThread().getContextClassLoader();
228     Thread.currentThread().setContextClassLoader(LITE_CLASS_LOADER);
229     try {
230       runTestMethod(LITE_CLASS_LOADER, InnerLiteTest.class);
231     } finally {
232       Thread.currentThread().setContextClassLoader(storedClassLoader);
233     }
234     try {
235       runTestMethod(storedClassLoader, InnerTest.class);
236     } finally {
237       Thread.currentThread().setContextClassLoader(storedClassLoader);
238     }
239   }
240 
runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass)241   private void runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass)
242       throws Exception {
243     classLoader.loadClass(ExtensionRegistryFactory.class.getName());
244     Class<?> test = classLoader.loadClass(testClass.getName());
245     String testName = getName();
246     test.getMethod(testName).invoke(test.getDeclaredConstructor().newInstance());
247   }
248 
249   /**
250    * Constructs a custom ClassLoader blacklisting the classes which are inspected in the SUT to
251    * determine the Lite/non-Lite runtime.
252    */
getLiteOnlyClassLoader()253   private static ClassLoader getLiteOnlyClassLoader() {
254     ClassLoader testClassLoader = ExtensionRegistryFactoryTest.class.getClassLoader();
255     final Set<String> classNamesNotInLite =
256         Collections.unmodifiableSet(
257             new HashSet<String>(
258                 Arrays.asList(
259                     ExtensionRegistryFactory.FULL_REGISTRY_CLASS_NAME,
260                     ExtensionRegistry.EXTENSION_CLASS_NAME)));
261 
262     // Construct a URLClassLoader delegating to the system ClassLoader, and looking up classes
263     // in jar files based on the URLs already configured for this test's UrlClassLoader.
264     // Certain classes throw a ClassNotFoundException by design.
265     return new URLClassLoader(
266         ((URLClassLoader) testClassLoader).getURLs(), ClassLoader.getSystemClassLoader()) {
267       @Override
268       public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
269         if (classNamesNotInLite.contains(name)) {
270           throw new ClassNotFoundException("Class deliberately blacklisted by test.");
271         }
272         Class<?> loadedClass = null;
273         try {
274           loadedClass = findLoadedClass(name);
275           if (loadedClass == null) {
276             loadedClass = findClass(name);
277             if (resolve) {
278               resolveClass(loadedClass);
279             }
280           }
281         } catch (ClassNotFoundException | SecurityException e) {
282           // Java 8+ would throw a SecurityException if we attempt to find a loaded class from
283           // java.lang.* package. We don't really care about those anyway, so just delegate to the
284           // parent class loader.
285           loadedClass = super.loadClass(name, resolve);
286         }
287         return loadedClass;
288       }
289     };
290   }
291 }
292