• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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