1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import static com.google.common.truth.Truth.assertThat; 11 import static com.google.common.truth.Truth.assertWithMessage; 12 13 import com.google.common.collect.ImmutableSet; 14 import com.google.common.reflect.ClassPath; 15 import protobuf_unittest.NonNestedExtension; 16 import protobuf_unittest.NonNestedExtensionLite; 17 import java.io.IOException; 18 import java.lang.reflect.Method; 19 import java.net.URL; 20 import java.net.URLClassLoader; 21 import junit.framework.Test; 22 import junit.framework.TestCase; 23 import junit.framework.TestSuite; 24 import org.junit.Ignore; 25 26 /** 27 * Tests for {@link ExtensionRegistryFactory} and the {@link ExtensionRegistry} instances it 28 * creates. 29 * 30 * <p>This test simulates the runtime behaviour of the ExtensionRegistryFactory by delegating test 31 * definitions to two inner classes {@link InnerTest} and {@link InnerLiteTest}, the latter of which 32 * is executed using a custom ClassLoader, simulating the ProtoLite environment. 33 * 34 * <p>The test mechanism employed here is based on the pattern in {@code 35 * com.google.common.util.concurrent.AbstractFutureFallbackAtomicHelperTest} 36 * 37 * <p>This test is temporarily disabled while we figure out how to fix the class loading used for 38 * testing lite functionality. 39 */ 40 @SuppressWarnings("JUnit4ClassUsedInJUnit3") 41 @Ignore 42 public class ExtensionRegistryFactoryTest extends TestCase { 43 44 // A classloader which blacklists some non-Lite classes. 45 private static final ClassLoader LITE_CLASS_LOADER = getLiteOnlyClassLoader(); 46 47 /** Defines the set of test methods which will be run. */ 48 static interface RegistryTests { testCreate()49 void testCreate(); 50 testEmpty()51 void testEmpty(); 52 testIsFullRegistry()53 void testIsFullRegistry(); 54 testAdd()55 void testAdd(); 56 testAdd_immutable()57 void testAdd_immutable(); 58 } 59 60 /** Test implementations for the non-Lite usage of ExtensionRegistryFactory. */ 61 public static class InnerTest implements RegistryTests { 62 63 @Override testCreate()64 public void testCreate() { 65 ExtensionRegistryLite registry = ExtensionRegistryFactory.create(); 66 67 assertThat(registry.getClass()).isEqualTo(ExtensionRegistry.class); 68 } 69 70 @Override testEmpty()71 public void testEmpty() { 72 ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty(); 73 74 assertThat(emptyRegistry.getClass()).isEqualTo(ExtensionRegistry.class); 75 assertThat(emptyRegistry).isEqualTo(ExtensionRegistry.EMPTY_REGISTRY); 76 } 77 78 @Override testIsFullRegistry()79 public void testIsFullRegistry() { 80 ExtensionRegistryLite registry = ExtensionRegistryFactory.create(); 81 assertThat(ExtensionRegistryFactory.isFullRegistry(registry)).isTrue(); 82 } 83 84 @Override testAdd()85 public void testAdd() { 86 ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance(); 87 NonNestedExtensionLite.registerAllExtensions(registry1); 88 registry1.add(NonNestedExtensionLite.nonNestedExtensionLite); 89 90 ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance(); 91 NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2); 92 registry2.add(NonNestedExtension.nonNestedExtension); 93 94 ExtensionRegistry fullRegistry1 = (ExtensionRegistry) registry1; 95 ExtensionRegistry fullRegistry2 = (ExtensionRegistry) registry2; 96 97 assertWithMessage("Test is using a non-lite extension") 98 .that(NonNestedExtensionLite.nonNestedExtensionLite.getClass()) 99 .isInstanceOf(GeneratedMessageLite.GeneratedExtension.class); 100 assertWithMessage("Extension is not registered in masqueraded full registry") 101 .that(fullRegistry1.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension")) 102 .isNull(); 103 GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?> 104 extension = 105 registry1.findLiteExtensionByNumber( 106 NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1); 107 assertWithMessage("Extension registered in lite registry").that(extension).isNotNull(); 108 109 assertWithMessage("Test is using a non-lite extension") 110 .that(Extension.class.isAssignableFrom(NonNestedExtension.nonNestedExtension.getClass())) 111 .isTrue(); 112 assertWithMessage("Extension is registered in masqueraded full registry") 113 .that(fullRegistry2.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension")) 114 .isNotNull(); 115 } 116 117 @Override testAdd_immutable()118 public void testAdd_immutable() { 119 ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance().getUnmodifiable(); 120 try { 121 NonNestedExtensionLite.registerAllExtensions(registry1); 122 assertWithMessage("expected exception").fail(); 123 } catch (UnsupportedOperationException expected) { 124 } 125 try { 126 registry1.add(NonNestedExtensionLite.nonNestedExtensionLite); 127 assertWithMessage("expected exception").fail(); 128 } catch (UnsupportedOperationException expected) { 129 } 130 131 ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance().getUnmodifiable(); 132 try { 133 NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2); 134 assertWithMessage("expected exception").fail(); 135 } catch (IllegalArgumentException expected) { 136 } 137 try { 138 registry2.add(NonNestedExtension.nonNestedExtension); 139 assertWithMessage("expected exception").fail(); 140 } catch (IllegalArgumentException expected) { 141 } 142 } 143 } 144 145 /** Test implementations for the Lite usage of ExtensionRegistryFactory. */ 146 public static final class InnerLiteTest implements RegistryTests { 147 148 @Override testCreate()149 public void testCreate() { 150 ExtensionRegistryLite registry = ExtensionRegistryFactory.create(); 151 152 assertThat(registry.getClass()).isEqualTo(ExtensionRegistryLite.class); 153 } 154 155 @Override testEmpty()156 public void testEmpty() { 157 ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty(); 158 159 assertThat(emptyRegistry.getClass()).isEqualTo(ExtensionRegistryLite.class); 160 assertThat(emptyRegistry).isEqualTo(ExtensionRegistryLite.EMPTY_REGISTRY_LITE); 161 } 162 163 @Override testIsFullRegistry()164 public void testIsFullRegistry() { 165 ExtensionRegistryLite registry = ExtensionRegistryFactory.create(); 166 assertThat(ExtensionRegistryFactory.isFullRegistry(registry)).isFalse(); 167 } 168 169 @Override testAdd()170 public void testAdd() { 171 ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance(); 172 NonNestedExtensionLite.registerAllExtensions(registry); 173 GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?> 174 extension = 175 registry.findLiteExtensionByNumber( 176 NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1); 177 assertWithMessage("Extension is registered in Lite registry").that(extension).isNotNull(); 178 } 179 180 @Override testAdd_immutable()181 public void testAdd_immutable() { 182 ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance().getUnmodifiable(); 183 try { 184 NonNestedExtensionLite.registerAllExtensions(registry); 185 assertWithMessage("expected exception").fail(); 186 } catch (UnsupportedOperationException expected) { 187 } 188 } 189 } 190 191 /** Defines a suite of tests which the JUnit3 runner retrieves by reflection. */ suite()192 public static Test suite() { 193 TestSuite suite = new TestSuite(); 194 for (Method method : RegistryTests.class.getMethods()) { 195 suite.addTest(TestSuite.createTest(ExtensionRegistryFactoryTest.class, method.getName())); 196 } 197 return suite; 198 } 199 200 /** 201 * Sequentially runs first the Lite and then the non-Lite test variant via classloader 202 * manipulation. 203 */ 204 @Override runTest()205 public void runTest() throws Exception { 206 ClassLoader storedClassLoader = Thread.currentThread().getContextClassLoader(); 207 Thread.currentThread().setContextClassLoader(LITE_CLASS_LOADER); 208 try { 209 runTestMethod(LITE_CLASS_LOADER, InnerLiteTest.class); 210 } finally { 211 Thread.currentThread().setContextClassLoader(storedClassLoader); 212 } 213 try { 214 runTestMethod(storedClassLoader, InnerTest.class); 215 } finally { 216 Thread.currentThread().setContextClassLoader(storedClassLoader); 217 } 218 } 219 runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass)220 private void runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass) 221 throws Exception { 222 classLoader.loadClass(ExtensionRegistryFactory.class.getName()); 223 Class<?> test = classLoader.loadClass(testClass.getName()); 224 String testName = getName(); 225 test.getMethod(testName).invoke(test.getDeclaredConstructor().newInstance()); 226 } 227 228 /** 229 * Constructs a custom ClassLoader blacklisting the classes which are inspected in the SUT to 230 * determine the Lite/non-Lite runtime. 231 */ getLiteOnlyClassLoader()232 private static ClassLoader getLiteOnlyClassLoader() { 233 234 ImmutableSet<ClassPath.ClassInfo> classes = ImmutableSet.of(); 235 try { 236 classes = ClassPath.from(ExtensionRegistryFactoryTest.class.getClassLoader()).getAllClasses(); 237 } catch (IOException ex) { 238 throw new RuntimeException(ex); 239 } 240 URL[] urls = new URL[classes.size()]; 241 int i = 0; 242 for (ClassPath.ClassInfo classInfo : classes) { 243 urls[i++] = classInfo.url(); 244 } 245 final ImmutableSet<String> classNamesNotInLite = 246 ImmutableSet.of( 247 ExtensionRegistryFactory.FULL_REGISTRY_CLASS_NAME, 248 ExtensionRegistry.EXTENSION_CLASS_NAME); 249 250 // Construct a URLClassLoader delegating to the system ClassLoader, and looking up classes 251 // in jar files based on the URLs already configured for this test's UrlClassLoader. 252 // Certain classes throw a ClassNotFoundException by design. 253 return new URLClassLoader(urls, ClassLoader.getSystemClassLoader()) { 254 @Override 255 public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 256 if (classNamesNotInLite.contains(name)) { 257 throw new ClassNotFoundException("Class deliberately blacklisted by test."); 258 } 259 Class<?> loadedClass = null; 260 try { 261 loadedClass = findLoadedClass(name); 262 if (loadedClass == null) { 263 loadedClass = findClass(name); 264 if (resolve) { 265 resolveClass(loadedClass); 266 } 267 } 268 } catch (ClassNotFoundException | SecurityException e) { 269 // Java 8+ would throw a SecurityException if we attempt to find a loaded class from 270 // java.lang.* package. We don't really care about those anyway, so just delegate to the 271 // parent class loader. 272 loadedClass = super.loadClass(name, resolve); 273 } 274 return loadedClass; 275 } 276 }; 277 } 278 } 279