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