9
9
import static org .mockito .Mockito .when ;
10
10
import static org .mockito .Mockito .withSettings ;
11
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 ;
12
17
import org .junit .After ;
13
18
import org .junit .Before ;
14
19
import org .junit .Test ;
@@ -18,11 +23,12 @@ public class PackagePrivateWithContextClassLoaderTest {
18
23
19
24
private ClassLoader oldContextClassloader ;
20
25
21
- public static class PublicClass {
22
-
26
+ public abstract static class PublicClass {
23
27
int packagePrivateMethod () {
24
28
return 0 ;
25
29
}
30
+
31
+ abstract void packagePrivateAbstractMethod ();
26
32
}
27
33
28
34
public interface PublicInterface {}
@@ -67,4 +73,102 @@ public void should_be_able_to_mock_package_private_extra_interface() throws Exce
67
73
PublicInterface .class ,
68
74
withSettings ().extraInterfaces (PackagePrivateInterface .class ));
69
75
}
76
+
77
+ /**
78
+ * In this test we have a class that delegates loading of mockito/JDK classes to its parent,
79
+ * but defines in its own for others. If mockito selects the defining classloader of the mock
80
+ * to the classloader of mockito, calling the abstract package-private method will fail - the
81
+ * defining classloader of the mocked type's package is different from the generated mock class
82
+ * package. Because the nonDelegatingLoader is a child of mockito's loader, it's more specific
83
+ * and should be preferred.
84
+ */
85
+ @ Test
86
+ public void classloader_with_parent_but_does_not_delegate () throws Exception {
87
+ ClassLoader nonDelegatingLoader = new NotAlwaysDelegatingClassLoader ();
88
+ Thread .currentThread ().setContextClassLoader (nonDelegatingLoader );
89
+ Class <?> loaded =
90
+ Class .forName (LoadedByCustomLoader .class .getName (), false , nonDelegatingLoader );
91
+ Method attemptMock = loaded .getDeclaredMethod ("attemptMock" );
92
+ attemptMock .invoke (null );
93
+ }
94
+
95
+ public static class LoadedByCustomLoader {
96
+ public static void attemptMock () {
97
+ PublicClass mock = mock (PublicClass .class );
98
+ mock .packagePrivateAbstractMethod ();
99
+ }
100
+ }
101
+
102
+ /**
103
+ * This classloader has a parent, but doesn't always delegate to it.
104
+ */
105
+ public static final class NotAlwaysDelegatingClassLoader extends ClassLoader {
106
+
107
+ /**
108
+ * Initial size of buffer used to read class data.
109
+ */
110
+ /* Note: should be enough for most classes, and is not a hard limit. */
111
+ private static final int BUF_SIZE = 4096 ;
112
+
113
+ public NotAlwaysDelegatingClassLoader () {
114
+ super (NotAlwaysDelegatingClassLoader .class .getClassLoader ());
115
+ }
116
+
117
+ @ Override
118
+ protected Class <?> loadClass (String name , boolean resolve ) throws ClassNotFoundException {
119
+ // First, check if the class has already been loaded. If not, load it
120
+ // ourselves or delegate to the parent.
121
+ Class <?> result = findLoadedClass (name );
122
+ if (result == null ) {
123
+ // All classes defined in this testsuite should be loaded by this classloader,
124
+ // but any other class (e.g. those coming from java.* or org.mockito.* packages)
125
+ // will be loaded by the parent.
126
+ if (name .startsWith ("org.mockitousage." )) {
127
+ result = findClass (name );
128
+ } else {
129
+ return super .loadClass (name , resolve );
130
+ }
131
+ }
132
+ if (resolve ) {
133
+ resolveClass (result );
134
+ }
135
+ return result ;
136
+ }
137
+
138
+ @ Override
139
+ public Class <?> findClass (String className ) throws ClassNotFoundException {
140
+ try {
141
+ // Create a package for this class, unless it's in the default package.
142
+ int dotpos = className .lastIndexOf ('.' );
143
+ if (dotpos != -1 ) {
144
+ String pkgname = className .substring (0 , dotpos );
145
+ if (getPackage (pkgname ) == null ) {
146
+ definePackage (pkgname , null , null , null , null , null , null , null );
147
+ }
148
+ }
149
+ String resourceName = className .replace ('.' , File .separatorChar ) + ".class" ;
150
+ InputStream input = getSystemResourceAsStream (resourceName );
151
+ if (input == null ) {
152
+ throw new ClassNotFoundException ("Couldn't find resource " + resourceName );
153
+ }
154
+ byte [] classData = loadClassData (input );
155
+ return defineClass (className , classData , 0 , classData .length , null );
156
+ } catch (IOException e ) {
157
+ throw new ClassNotFoundException ("Cannot load " + className , e );
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Load class data from a given input stream.
163
+ */
164
+ private byte [] loadClassData (InputStream input ) throws IOException {
165
+ ByteArrayOutputStream output = new ByteArrayOutputStream (BUF_SIZE );
166
+ byte [] buffer = new byte [BUF_SIZE ];
167
+ int readCount ;
168
+ while ((readCount = input .read (buffer , 0 , BUF_SIZE )) >= 0 ) {
169
+ output .write (buffer , 0 , readCount );
170
+ }
171
+ return output .toByteArray ();
172
+ }
173
+ }
70
174
}
0 commit comments