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