1package org.mockitoutil;
2
3import java.util.concurrent.atomic.AtomicBoolean;
4import org.assertj.core.api.Assertions;
5import org.junit.Test;
6import org.mockito.Mockito;
7
8import static org.assertj.core.api.Assertions.assertThat;
9import static org.junit.Assert.fail;
10import static org.mockitoutil.ClassLoaders.currentClassLoader;
11import static org.mockitoutil.ClassLoaders.excludingClassLoader;
12import static org.mockitoutil.ClassLoaders.isolatedClassLoader;
13import static org.mockitoutil.ClassLoaders.jdkClassLoader;
14
15public class ClassLoadersTest {
16
17    public static final String CLASS_NAME_DEPENDING_ON_INTERFACE = "org.mockitoutil.ClassLoadersTest$ClassUsingInterface1";
18    public static final String INTERFACE_NAME = "org.mockitoutil.ClassLoadersTest$Interface1";
19
20    @Test(expected = ClassNotFoundException.class)
21    public void isolated_class_loader_cannot_load_classes_when_no_given_prefix() throws Exception {
22        // given
23        ClassLoader cl = isolatedClassLoader().build();
24
25        // when
26        cl.loadClass("org.mockito.Mockito");
27
28        // then raises CNFE
29    }
30
31    @Test
32    public void isolated_class_loader_cannot_load_classes_if_no_code_source_path() throws Exception {
33        // given
34        ClassLoader cl = isolatedClassLoader()
35                .withPrivateCopyOf(CLASS_NAME_DEPENDING_ON_INTERFACE)
36                .build();
37
38        // when
39        try {
40            cl.loadClass(CLASS_NAME_DEPENDING_ON_INTERFACE);
41            fail();
42        } catch (ClassNotFoundException e) {
43            // then
44            assertThat(e).hasMessageContaining(CLASS_NAME_DEPENDING_ON_INTERFACE);
45        }
46    }
47
48    @Test
49    public void isolated_class_loader_cannot_load_classes_if_dependent_classes_do_not_match_the_prefixes() throws Exception {
50        // given
51        ClassLoader cl = isolatedClassLoader()
52                .withCurrentCodeSourceUrls()
53                .withPrivateCopyOf(CLASS_NAME_DEPENDING_ON_INTERFACE)
54                .build();
55
56        // when
57        try {
58            cl.loadClass(CLASS_NAME_DEPENDING_ON_INTERFACE);
59            fail();
60        } catch (NoClassDefFoundError e) {
61            // then
62            assertThat(e).hasMessageContaining("org/mockitoutil/ClassLoadersTest$Interface1");
63        }
64    }
65
66    @Test
67    public void isolated_class_loader_can_load_classes_when_dependent_classes_are_matching_the_prefixes() throws Exception {
68        // given
69        ClassLoader cl = isolatedClassLoader()
70                .withCurrentCodeSourceUrls()
71                .withPrivateCopyOf(CLASS_NAME_DEPENDING_ON_INTERFACE)
72                .withPrivateCopyOf(INTERFACE_NAME)
73                .build();
74
75        // when
76        Class<?> aClass = cl.loadClass(CLASS_NAME_DEPENDING_ON_INTERFACE);
77
78        // then
79        assertThat(aClass).isNotNull();
80        assertThat(aClass.getClassLoader()).isEqualTo(cl);
81        assertThat(aClass.getInterfaces()[0].getClassLoader()).isEqualTo(cl);
82    }
83
84    @Test
85    public void isolated_class_loader_can_load_classes_isolated_classes_in_isolation() throws Exception {
86        // given
87        ClassLoader cl = isolatedClassLoader()
88                .withCurrentCodeSourceUrls()
89                .withPrivateCopyOf(ClassLoadersTest.class.getPackage().getName())
90                .build();
91
92        // when
93        Class<?> aClass = cl.loadClass(AClass.class.getName());
94
95        // then
96        assertThat(aClass).isNotNull();
97        assertThat(aClass).isNotSameAs(AClass.class);
98        assertThat(aClass.getClassLoader()).isEqualTo(cl);
99    }
100
101    @Test
102    public void isolated_class_loader_cannot_load_classes_if_prefix_excluded() throws Exception {
103        // given
104        ClassLoader cl = isolatedClassLoader()
105                .withCurrentCodeSourceUrls()
106                .withPrivateCopyOf(ClassLoadersTest.class.getPackage().getName())
107                .without(AClass.class.getName())
108                .build();
109
110        // when
111        try {
112            cl.loadClass(AClass.class.getName());
113            fail();
114        } catch (ClassNotFoundException e) {
115            // then
116            assertThat(e).hasMessageContaining("org.mockitoutil")
117                         .hasMessageContaining(AClass.class.getName());
118        }
119    }
120
121    @Test
122    public void isolated_class_loader_has_no_parent() throws Exception {
123        ClassLoader cl = isolatedClassLoader()
124                .withCurrentCodeSourceUrls()
125                .withPrivateCopyOf(CLASS_NAME_DEPENDING_ON_INTERFACE)
126                .withPrivateCopyOf(INTERFACE_NAME)
127                .build();
128
129        assertThat(cl.getParent()).isNull();
130    }
131
132    @Test(expected = ClassNotFoundException.class)
133    public void excluding_class_loader_cannot_load_classes_when_no_correct_source_url_set() throws Exception {
134        // given
135        ClassLoader cl = excludingClassLoader()
136                .withCodeSourceUrlOf(this.getClass())
137                .build();
138
139        // when
140        cl.loadClass("org.mockito.Mockito");
141
142        // then class CNFE
143    }
144
145    @Test
146    public void excluding_class_loader_can_load_classes_when_correct_source_url_set() throws Exception {
147        // given
148        ClassLoader cl = excludingClassLoader()
149                .withCodeSourceUrlOf(Mockito.class)
150                .build();
151
152        // when
153        cl.loadClass("org.mockito.Mockito");
154
155        // then class successfully loaded
156    }
157
158    @Test
159    public void excluding_class_loader_cannot_load_class_when_excluded_prefix_match_class_to_load() throws Exception {
160        // given
161        ClassLoader cl = excludingClassLoader()
162                .withCodeSourceUrlOf(Mockito.class)
163                .without("org.mockito.BDDMockito")
164                .build();
165
166        cl.loadClass("org.mockito.Mockito");
167
168        // when
169        try {
170            cl.loadClass("org.mockito.BDDMockito");
171            fail("should have raise a ClassNotFoundException");
172        } catch (ClassNotFoundException e) {
173            assertThat(e.getMessage()).contains("org.mockito.BDDMockito");
174        }
175
176        // then class successfully loaded
177    }
178
179    @Test
180    public void can_not_load_a_class_not_previously_registered_in_builder() throws Exception {
181        // given
182        ClassLoader cl = ClassLoaders
183                .inMemoryClassLoader()
184                .withClassDefinition("yop.Dude", SimpleClassGenerator.makeMarkerInterface("yop.Dude"))
185                .build();
186
187        // when
188        try {
189            cl.loadClass("not.Defined");
190            fail();
191        } catch (ClassNotFoundException e) {
192            // then
193            assertThat(e.getMessage()).contains("not.Defined");
194        }
195    }
196
197    @Test
198    public void can_load_a_class_in_memory_from_bytes() throws Exception {
199        // given
200        ClassLoader cl = ClassLoaders
201                .inMemoryClassLoader()
202                .withClassDefinition("yop.Dude", SimpleClassGenerator.makeMarkerInterface("yop.Dude"))
203                .build();
204
205        // when
206        Class<?> aClass = cl.loadClass("yop.Dude");
207
208        // then
209        assertThat(aClass).isNotNull();
210        assertThat(aClass.getClassLoader()).isEqualTo(cl);
211        assertThat(aClass.getName()).isEqualTo("yop.Dude");
212    }
213
214    @Test
215    public void cannot_load_a_class_file_not_in_parent() throws Exception {
216        // given
217        ClassLoader cl = ClassLoaders
218                .inMemoryClassLoader()
219                .withParent(jdkClassLoader())
220                .build();
221
222        cl.loadClass("java.lang.String");
223
224        try {
225            // when
226            cl.loadClass("org.mockito.Mockito");
227            fail("should have not found Mockito class");
228        } catch (ClassNotFoundException e) {
229            // then
230            assertThat(e.getMessage()).contains("org.mockito.Mockito");
231        }
232    }
233
234    @Test
235    public void can_list_all_classes_reachable_in_a_classloader() throws Exception {
236        ClassLoader classLoader = ClassLoaders.inMemoryClassLoader()
237                                              .withParent(jdkClassLoader())
238                                              .withClassDefinition("a.A", SimpleClassGenerator.makeMarkerInterface("a.A"))
239                                              .withClassDefinition("a.b.B", SimpleClassGenerator.makeMarkerInterface("a.b.B"))
240                                              .withClassDefinition("c.C", SimpleClassGenerator.makeMarkerInterface("c.C"))
241//                .withCodeSourceUrlOf(ClassLoaders.class)
242                                              .build();
243
244        assertThat(ClassLoaders.in(classLoader).listOwnedClasses()).containsOnly("a.A", "a.b.B", "c.C");
245        assertThat(ClassLoaders.in(classLoader).omit("b", "c").listOwnedClasses()).containsOnly("a.A");
246    }
247
248    @Test
249    public void return_bootstrap_classloader() throws Exception {
250        assertThat(jdkClassLoader()).isNotEqualTo(Mockito.class.getClassLoader());
251        assertThat(jdkClassLoader()).isNotEqualTo(ClassLoaders.class.getClassLoader());
252        assertThat(jdkClassLoader()).isEqualTo(Number.class.getClassLoader());
253        assertThat(jdkClassLoader()).isEqualTo(null);
254    }
255
256    @Test
257    public void return_current_classloader() throws Exception {
258        assertThat(currentClassLoader()).isEqualTo(this.getClass().getClassLoader());
259    }
260
261    @Test
262    public void can_run_in_given_classloader() throws Exception {
263        // given
264        final ClassLoader cl = isolatedClassLoader()
265                .withCurrentCodeSourceUrls()
266                .withCodeSourceUrlOf(Assertions.class)
267                .withPrivateCopyOf("org.assertj.core")
268                .withPrivateCopyOf(ClassLoadersTest.class.getPackage().getName())
269                .without(AClass.class.getName())
270                .build();
271
272        final AtomicBoolean executed = new AtomicBoolean(false);
273
274        // when
275        ClassLoaders.using(cl).execute(new Runnable() {
276            @Override
277            public void run() {
278                assertThat(this.getClass().getClassLoader()).describedAs("runnable is reloaded in given classloader").isEqualTo(cl);
279                assertThat(Thread.currentThread().getContextClassLoader()).describedAs("Thread context classloader is using given classloader").isEqualTo(cl);
280
281                try {
282                    assertThat(Thread.currentThread()
283                                     .getContextClassLoader()
284                                     .loadClass("java.lang.String"))
285                            .describedAs("can load JDK type")
286                            .isNotNull();
287                    assertThat(Thread.currentThread()
288                                     .getContextClassLoader()
289                                     .loadClass("org.mockitoutil.ClassLoadersTest$ClassUsingInterface1"))
290                            .describedAs("can load classloader types")
291                            .isNotNull();
292                } catch (ClassNotFoundException cnfe) {
293                    Assertions.fail("should not have raised a CNFE", cnfe);
294                }
295                executed.set(true);
296            }
297        });
298
299        // then
300        assertThat(executed.get()).isEqualTo(true);
301    }
302
303
304    @Test
305    public void cannot_load_runnable_in_given_classloader_if_some_type_cant_be_loaded() throws Exception {
306        // given
307        final ClassLoader cl = isolatedClassLoader()
308                .withCurrentCodeSourceUrls()
309                .withPrivateCopyOf(ClassLoadersTest.class.getPackage().getName())
310                .without(AClass.class.getName())
311                .build();
312
313        // when
314        try {
315            ClassLoaders.using(cl).execute(new Runnable() {
316                @Override
317                public void run() {
318                    AClass cant_be_found = new AClass();
319                }
320            });
321            Assertions.fail("should have raised a ClassNotFoundException");
322        } catch (IllegalStateException ise) {
323            // then
324            assertThat(ise).hasCauseInstanceOf(NoClassDefFoundError.class)
325                           .hasMessageContaining("AClass");
326        }
327    }
328
329    @SuppressWarnings("unused")
330    static class AClass {
331    }
332
333    @SuppressWarnings("unused")
334    static class ClassUsingInterface1 implements Interface1 {
335    }
336
337    @SuppressWarnings("unused")
338    interface Interface1 {
339    }
340}
341