• 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 protobuf_unittest.NonNestedExtension;
34 import protobuf_unittest.NonNestedExtensionLite;
35 import java.lang.reflect.Method;
36 import java.net.URLClassLoader;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.HashSet;
40 import java.util.Set;
41 import junit.framework.Test;
42 import junit.framework.TestCase;
43 import junit.framework.TestSuite;
44 import org.junit.Ignore;
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 @SuppressWarnings("JUnit4ClassUsedInJUnit3")
62 @Ignore
63 public class ExtensionRegistryFactoryTest extends TestCase {
64 
65   // A classloader which blacklists some non-Lite classes.
66   private static final ClassLoader LITE_CLASS_LOADER = getLiteOnlyClassLoader();
67 
68   /** Defines the set of test methods which will be run. */
69   static interface RegistryTests {
testCreate()70     void testCreate();
71 
testEmpty()72     void testEmpty();
73 
testIsFullRegistry()74     void testIsFullRegistry();
75 
testAdd()76     void testAdd();
77 
testAdd_immutable()78     void testAdd_immutable();
79   }
80 
81   /** Test implementations for the non-Lite usage of ExtensionRegistryFactory. */
82   public static class InnerTest implements RegistryTests {
83 
84     @Override
testCreate()85     public void testCreate() {
86       ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
87 
88       assertEquals(registry.getClass(), ExtensionRegistry.class);
89     }
90 
91     @Override
testEmpty()92     public void testEmpty() {
93       ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty();
94 
95       assertEquals(emptyRegistry.getClass(), ExtensionRegistry.class);
96       assertEquals(emptyRegistry, ExtensionRegistry.EMPTY_REGISTRY);
97     }
98 
99     @Override
testIsFullRegistry()100     public void testIsFullRegistry() {
101       ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
102       assertTrue(ExtensionRegistryFactory.isFullRegistry(registry));
103     }
104 
105     @Override
testAdd()106     public void testAdd() {
107       ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance();
108       NonNestedExtensionLite.registerAllExtensions(registry1);
109       registry1.add(NonNestedExtensionLite.nonNestedExtensionLite);
110 
111       ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance();
112       NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2);
113       registry2.add(NonNestedExtension.nonNestedExtension);
114 
115       ExtensionRegistry fullRegistry1 = (ExtensionRegistry) registry1;
116       ExtensionRegistry fullRegistry2 = (ExtensionRegistry) registry2;
117 
118       assertTrue(
119           "Test is using a non-lite extension",
120           GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(
121               NonNestedExtensionLite.nonNestedExtensionLite.getClass()));
122       assertNull(
123           "Extension is not registered in masqueraded full registry",
124           fullRegistry1.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension"));
125       GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?>
126           extension =
127               registry1.findLiteExtensionByNumber(
128                   NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1);
129       assertNotNull("Extension registered in lite registry", extension);
130 
131       assertTrue(
132           "Test is using a non-lite extension",
133           Extension.class.isAssignableFrom(NonNestedExtension.nonNestedExtension.getClass()));
134       assertNotNull(
135           "Extension is registered in masqueraded full registry",
136           fullRegistry2.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension"));
137     }
138 
139     @Override
testAdd_immutable()140     public void testAdd_immutable() {
141       ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance().getUnmodifiable();
142       try {
143         NonNestedExtensionLite.registerAllExtensions(registry1);
144         fail();
145       } catch (UnsupportedOperationException expected) {
146       }
147       try {
148         registry1.add(NonNestedExtensionLite.nonNestedExtensionLite);
149         fail();
150       } catch (UnsupportedOperationException expected) {
151       }
152 
153       ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance().getUnmodifiable();
154       try {
155         NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2);
156         fail();
157       } catch (IllegalArgumentException expected) {
158       }
159       try {
160         registry2.add(NonNestedExtension.nonNestedExtension);
161         fail();
162       } catch (IllegalArgumentException expected) {
163       }
164     }
165   }
166 
167   /** Test implementations for the Lite usage of ExtensionRegistryFactory. */
168   public static final class InnerLiteTest implements RegistryTests {
169 
170     @Override
testCreate()171     public void testCreate() {
172       ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
173 
174       assertEquals(registry.getClass(), ExtensionRegistryLite.class);
175     }
176 
177     @Override
testEmpty()178     public void testEmpty() {
179       ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty();
180 
181       assertEquals(emptyRegistry.getClass(), ExtensionRegistryLite.class);
182       assertEquals(emptyRegistry, ExtensionRegistryLite.EMPTY_REGISTRY_LITE);
183     }
184 
185     @Override
testIsFullRegistry()186     public void testIsFullRegistry() {
187       ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
188       assertFalse(ExtensionRegistryFactory.isFullRegistry(registry));
189     }
190 
191     @Override
testAdd()192     public void testAdd() {
193       ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
194       NonNestedExtensionLite.registerAllExtensions(registry);
195       GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?>
196           extension =
197               registry.findLiteExtensionByNumber(
198                   NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1);
199       assertNotNull("Extension is registered in Lite registry", extension);
200     }
201 
202     @Override
testAdd_immutable()203     public void testAdd_immutable() {
204       ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance().getUnmodifiable();
205       try {
206         NonNestedExtensionLite.registerAllExtensions(registry);
207         fail();
208       } catch (UnsupportedOperationException expected) {
209       }
210     }
211   }
212 
213   /** Defines a suite of tests which the JUnit3 runner retrieves by reflection. */
suite()214   public static Test suite() {
215     TestSuite suite = new TestSuite();
216     for (Method method : RegistryTests.class.getMethods()) {
217       suite.addTest(TestSuite.createTest(ExtensionRegistryFactoryTest.class, method.getName()));
218     }
219     return suite;
220   }
221 
222   /**
223    * Sequentially runs first the Lite and then the non-Lite test variant via classloader
224    * manipulation.
225    */
226   @Override
runTest()227   public void runTest() throws Exception {
228     ClassLoader storedClassLoader = Thread.currentThread().getContextClassLoader();
229     Thread.currentThread().setContextClassLoader(LITE_CLASS_LOADER);
230     try {
231       runTestMethod(LITE_CLASS_LOADER, InnerLiteTest.class);
232     } finally {
233       Thread.currentThread().setContextClassLoader(storedClassLoader);
234     }
235     try {
236       runTestMethod(storedClassLoader, InnerTest.class);
237     } finally {
238       Thread.currentThread().setContextClassLoader(storedClassLoader);
239     }
240   }
241 
runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass)242   private void runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass)
243       throws Exception {
244     classLoader.loadClass(ExtensionRegistryFactory.class.getName());
245     Class<?> test = classLoader.loadClass(testClass.getName());
246     String testName = getName();
247     test.getMethod(testName).invoke(test.getDeclaredConstructor().newInstance());
248   }
249 
250   /**
251    * Constructs a custom ClassLoader blacklisting the classes which are inspected in the SUT to
252    * determine the Lite/non-Lite runtime.
253    */
getLiteOnlyClassLoader()254   private static ClassLoader getLiteOnlyClassLoader() {
255     ClassLoader testClassLoader = ExtensionRegistryFactoryTest.class.getClassLoader();
256     final Set<String> classNamesNotInLite =
257         Collections.unmodifiableSet(
258             new HashSet<String>(
259                 Arrays.asList(
260                     ExtensionRegistryFactory.FULL_REGISTRY_CLASS_NAME,
261                     ExtensionRegistry.EXTENSION_CLASS_NAME)));
262 
263     // Construct a URLClassLoader delegating to the system ClassLoader, and looking up classes
264     // in jar files based on the URLs already configured for this test's UrlClassLoader.
265     // Certain classes throw a ClassNotFoundException by design.
266     return new URLClassLoader(
267         ((URLClassLoader) testClassLoader).getURLs(), ClassLoader.getSystemClassLoader()) {
268       @Override
269       public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
270         if (classNamesNotInLite.contains(name)) {
271           throw new ClassNotFoundException("Class deliberately blacklisted by test.");
272         }
273         Class<?> loadedClass = null;
274         try {
275           loadedClass = findLoadedClass(name);
276           if (loadedClass == null) {
277             loadedClass = findClass(name);
278             if (resolve) {
279               resolveClass(loadedClass);
280             }
281           }
282         } catch (ClassNotFoundException | SecurityException e) {
283           // Java 8+ would throw a SecurityException if we attempt to find a loaded class from
284           // java.lang.* package. We don't really care about those anyway, so just delegate to the
285           // parent class loader.
286           loadedClass = super.loadClass(name, resolve);
287         }
288         return loadedClass;
289       }
290     };
291   }
292 }
293