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