1 /* 2 * Copyright (c) 2017 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockitousage.bugs.creation; 6 7 import static org.assertj.core.api.Assertions.assertThat; 8 import static org.mockito.Mockito.mock; 9 import static org.mockito.Mockito.when; 10 import static org.mockito.Mockito.withSettings; 11 12 import java.io.ByteArrayOutputStream; 13 import java.io.File; 14 import java.io.IOException; 15 import java.io.InputStream; 16 import java.lang.reflect.Method; 17 import org.junit.After; 18 import org.junit.Before; 19 import org.junit.Test; 20 21 /** Regression tests for issue #2303*/ 22 public class PackagePrivateWithContextClassLoaderTest { 23 24 private ClassLoader oldContextClassloader; 25 26 public abstract static class PublicClass { packagePrivateMethod()27 int packagePrivateMethod() { 28 return 0; 29 } 30 packagePrivateAbstractMethod()31 abstract void packagePrivateAbstractMethod(); 32 } 33 34 public interface PublicInterfaceWithPackagePrivateMethodParam { doSomething(PackagePrivateInterface i)35 void doSomething(PackagePrivateInterface i); 36 } 37 38 public interface PublicInterfaceWithPackagePrivateMethodReturn { doSomething()39 PackagePrivateInterface doSomething(); 40 } 41 42 public interface PublicInterfaceOverridesPackagePrivateMethodReturn { doSomething()43 PublicChildOfPackagePrivate doSomething(); 44 } 45 46 public interface PublicInterface {} 47 48 interface PackagePrivateInterface {} 49 50 public interface PublicChildOfPackagePrivate extends PackagePrivateInterface {} 51 52 static class PackagePrivateClass {} 53 54 @Before setUp()55 public void setUp() { 56 oldContextClassloader = Thread.currentThread().getContextClassLoader(); 57 Thread.currentThread().setContextClassLoader(new ClassLoader(oldContextClassloader) {}); 58 } 59 60 @After teardown()61 public void teardown() { 62 Thread.currentThread().setContextClassLoader(oldContextClassloader); 63 } 64 65 @Test should_be_able_to_mock_package_private_method()66 public void should_be_able_to_mock_package_private_method() throws Exception { 67 PublicClass publicClass = mock(PublicClass.class); 68 when(publicClass.packagePrivateMethod()).thenReturn(3); 69 assertThat(publicClass.packagePrivateMethod()).isEqualTo(3); 70 } 71 72 @Test should_be_able_to_mock_interface_method_package_private_param()73 public void should_be_able_to_mock_interface_method_package_private_param() throws Exception { 74 PublicInterfaceWithPackagePrivateMethodParam publicClass = 75 mock(PublicInterfaceWithPackagePrivateMethodParam.class); 76 publicClass.doSomething(null); 77 } 78 79 @Test should_be_able_to_mock_interface_method_package_private_return()80 public void should_be_able_to_mock_interface_method_package_private_return() throws Exception { 81 PublicInterfaceWithPackagePrivateMethodReturn publicClass = 82 mock(PublicInterfaceWithPackagePrivateMethodReturn.class); 83 PackagePrivateInterface packagePrivateInterface = publicClass.doSomething(); 84 } 85 86 @Test should_be_able_to_mock_interface_method_package_private_return_override()87 public void should_be_able_to_mock_interface_method_package_private_return_override() 88 throws Exception { 89 PublicInterfaceOverridesPackagePrivateMethodReturn publicClass = 90 mock(PublicInterfaceOverridesPackagePrivateMethodReturn.class); 91 PackagePrivateInterface packagePrivateInterface = publicClass.doSomething(); 92 } 93 94 @Test should_be_able_to_mock_package_private_class()95 public void should_be_able_to_mock_package_private_class() throws Exception { 96 PackagePrivateClass mock = mock(PackagePrivateClass.class); 97 } 98 99 @Test should_be_able_to_mock_package_private_interface()100 public void should_be_able_to_mock_package_private_interface() throws Exception { 101 PackagePrivateInterface mock = mock(PackagePrivateInterface.class); 102 } 103 104 @Test should_be_able_to_mock_package_private_extra_interface()105 public void should_be_able_to_mock_package_private_extra_interface() throws Exception { 106 PackagePrivateInterface mock = 107 (PackagePrivateInterface) 108 mock( 109 PublicInterface.class, 110 withSettings().extraInterfaces(PackagePrivateInterface.class)); 111 } 112 113 /** 114 * In this test we have a class that delegates loading of mockito/JDK classes to its parent, 115 * but defines in its own for others. If mockito selects the defining classloader of the mock 116 * to the classloader of mockito, calling the abstract package-private method will fail - the 117 * defining classloader of the mocked type's package is different from the generated mock class 118 * package. Because the nonDelegatingLoader is a child of mockito's loader, it's more specific 119 * and should be preferred. 120 */ 121 @Test classloader_with_parent_but_does_not_delegate()122 public void classloader_with_parent_but_does_not_delegate() throws Exception { 123 ClassLoader nonDelegatingLoader = new NotAlwaysDelegatingClassLoader(); 124 Thread.currentThread().setContextClassLoader(nonDelegatingLoader); 125 Class<?> loaded = 126 Class.forName(LoadedByCustomLoader.class.getName(), false, nonDelegatingLoader); 127 Method attemptMock = loaded.getDeclaredMethod("attemptMock"); 128 attemptMock.invoke(null); 129 } 130 131 public static class LoadedByCustomLoader { attemptMock()132 public static void attemptMock() { 133 PublicClass mock = mock(PublicClass.class); 134 mock.packagePrivateAbstractMethod(); 135 } 136 } 137 138 /** 139 * This classloader has a parent, but doesn't always delegate to it. 140 */ 141 public static final class NotAlwaysDelegatingClassLoader extends ClassLoader { 142 143 /** 144 * Initial size of buffer used to read class data. 145 */ 146 /* Note: should be enough for most classes, and is not a hard limit. */ 147 private static final int BUF_SIZE = 4096; 148 NotAlwaysDelegatingClassLoader()149 public NotAlwaysDelegatingClassLoader() { 150 super(NotAlwaysDelegatingClassLoader.class.getClassLoader()); 151 } 152 153 @Override loadClass(String name, boolean resolve)154 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 155 // First, check if the class has already been loaded. If not, load it 156 // ourselves or delegate to the parent. 157 Class<?> result = findLoadedClass(name); 158 if (result == null) { 159 // All classes defined in this testsuite should be loaded by this classloader, 160 // but any other class (e.g. those coming from java.* or org.mockito.* packages) 161 // will be loaded by the parent. 162 if (name.startsWith("org.mockitousage.")) { 163 result = findClass(name); 164 } else { 165 return super.loadClass(name, resolve); 166 } 167 } 168 if (resolve) { 169 resolveClass(result); 170 } 171 return result; 172 } 173 174 @Override findClass(String className)175 public Class<?> findClass(String className) throws ClassNotFoundException { 176 try { 177 // Create a package for this class, unless it's in the default package. 178 int dotpos = className.lastIndexOf('.'); 179 if (dotpos != -1) { 180 String pkgname = className.substring(0, dotpos); 181 if (getPackage(pkgname) == null) { 182 definePackage(pkgname, null, null, null, null, null, null, null); 183 } 184 } 185 String resourceName = className.replace('.', File.separatorChar) + ".class"; 186 InputStream input = getSystemResourceAsStream(resourceName); 187 if (input == null) { 188 throw new ClassNotFoundException("Couldn't find resource " + resourceName); 189 } 190 byte[] classData = loadClassData(input); 191 return defineClass(className, classData, 0, classData.length, null); 192 } catch (IOException e) { 193 throw new ClassNotFoundException("Cannot load " + className, e); 194 } 195 } 196 197 /** 198 * Load class data from a given input stream. 199 */ loadClassData(InputStream input)200 private byte[] loadClassData(InputStream input) throws IOException { 201 ByteArrayOutputStream output = new ByteArrayOutputStream(BUF_SIZE); 202 byte[] buffer = new byte[BUF_SIZE]; 203 int readCount; 204 while ((readCount = input.read(buffer, 0, BUF_SIZE)) >= 0) { 205 output.write(buffer, 0, readCount); 206 } 207 return output.toByteArray(); 208 } 209 } 210 } 211