1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.kuali.rice.test.runners;
18
19 import org.apache.commons.beanutils.MethodUtils;
20 import org.junit.After;
21 import org.junit.AfterClass;
22 import org.junit.Before;
23 import org.junit.BeforeClass;
24 import org.junit.ClassRule;
25 import org.junit.Ignore;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.junit.internal.AssumptionViolatedException;
29 import org.junit.internal.runners.model.EachTestNotifier;
30 import org.junit.internal.runners.model.ReflectiveCallable;
31 import org.junit.internal.runners.statements.ExpectException;
32 import org.junit.internal.runners.statements.Fail;
33 import org.junit.internal.runners.statements.FailOnTimeout;
34 import org.junit.internal.runners.statements.InvokeMethod;
35 import org.junit.internal.runners.statements.RunAfters;
36 import org.junit.internal.runners.statements.RunBefores;
37 import org.junit.rules.RunRules;
38 import org.junit.rules.TestRule;
39 import org.junit.runner.Description;
40 import org.junit.runner.JUnitCore;
41 import org.junit.runner.Result;
42 import org.junit.runner.RunWith;
43 import org.junit.runner.Runner;
44 import org.junit.runner.manipulation.Filter;
45 import org.junit.runner.manipulation.Filterable;
46 import org.junit.runner.manipulation.NoTestsRemainException;
47 import org.junit.runner.manipulation.Sortable;
48 import org.junit.runner.manipulation.Sorter;
49 import org.junit.runner.notification.Failure;
50 import org.junit.runner.notification.RunNotifier;
51 import org.junit.runner.notification.StoppedByUserException;
52 import org.junit.runners.model.FrameworkMethod;
53 import org.junit.runners.model.InitializationError;
54 import org.junit.runners.model.RunnerScheduler;
55 import org.junit.runners.model.Statement;
56 import org.junit.runners.model.TestClass;
57 import org.kuali.rice.core.api.util.ShadowingInstrumentableClassLoader;
58 import org.kuali.rice.test.MethodAware;
59
60 import java.lang.annotation.Annotation;
61 import java.lang.reflect.Method;
62 import java.util.ArrayList;
63 import java.util.Collections;
64 import java.util.Comparator;
65 import java.util.Iterator;
66 import java.util.List;
67
68 import static org.junit.internal.runners.rules.RuleFieldValidator.*;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public class LoadTimeWeavableTestRunner extends Runner implements Filterable, Sortable {
91
92 private static final String[] JUNIT_CLASSLOADER_EXCLUDES = { "org.junit.", "junit.framework." };
93
94 private final TestClass originalTestClass;
95 private TestClass fTestClass;
96 private Method currentMethod;
97
98
99
100 private static ClassLoader customLoader;
101
102 private Sorter fSorter = Sorter.NULL;
103
104 private List<FrameworkMethod> originalFilteredChildren = null;
105 private List<FrameworkMethod> filteredChildren = null;
106
107 private static final ThreadLocal<Boolean> runningBootstrapTest = new ThreadLocal<Boolean>() {
108 @Override
109 protected Boolean initialValue() {
110 return Boolean.FALSE;
111 }
112 };
113
114 private RunnerScheduler fScheduler = new RunnerScheduler() {
115 public void schedule(Runnable childStatement) {
116 childStatement.run();
117 }
118 public void finished() {
119
120 }
121 };
122
123
124
125
126 public LoadTimeWeavableTestRunner(Class<?> testClass) throws InitializationError {
127 this.originalTestClass = new TestClass(testClass);
128 if (LoadTimeWeavableTestRunner.customLoader == null) {
129 LoadTimeWeavableTestRunner.customLoader =
130 new ShadowingInstrumentableClassLoader(testClass.getClassLoader(), JUNIT_CLASSLOADER_EXCLUDES);
131 }
132 validate();
133 }
134
135 private TestClass getCustomTestClass(Class<?> originalTestClass, ClassLoader customLoader) {
136 try {
137 Class<?> newTestClass = customLoader.loadClass(originalTestClass.getName());
138 if (newTestClass == originalTestClass) {
139 throw new IllegalStateException(newTestClass.getName() + " loaded from custom class loader should have been a different instance but was the same!");
140 }
141 return new TestClass(newTestClass);
142 } catch (ClassNotFoundException e) {
143 throw new IllegalStateException("Failed to load test class from custom classloader: " + originalTestClass.getName());
144 }
145 }
146
147 protected ClassLoader getCustomClassLoader() {
148 return customLoader;
149 }
150
151
152
153
154
155
156
157
158
159
160
161 protected void validatePublicVoidNoArgMethods(Class<? extends Annotation> annotation,
162 boolean isStatic, List<Throwable> errors) {
163 List<FrameworkMethod> methods = getOriginalTestClass().getAnnotatedMethods(annotation);
164
165 for (FrameworkMethod eachTestMethod : methods) {
166 eachTestMethod.validatePublicVoidNoArg(isStatic, errors);
167 }
168 }
169
170 private void validateClassRules(List<Throwable> errors) {
171 CLASS_RULE_VALIDATOR.validate(getOriginalTestClass(), errors);
172 CLASS_RULE_METHOD_VALIDATOR.validate(getOriginalTestClass(), errors);
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192 protected Statement classBlock(final RunNotifier notifier) {
193 Statement statement = childrenInvoker(notifier);
194 statement = withBeforeClasses(statement);
195 statement = withAfterClasses(statement);
196 statement = withClassRules(statement);
197 return statement;
198 }
199
200
201
202
203
204
205 protected Statement withBeforeClasses(Statement statement) {
206 List<FrameworkMethod> befores = getTestClass()
207 .getAnnotatedMethods(BeforeClass.class);
208 return befores.isEmpty() ? statement :
209 new RunBefores(statement, befores, null);
210 }
211
212
213
214
215
216
217
218
219 protected Statement withAfterClasses(Statement statement) {
220 List<FrameworkMethod> afters = getTestClass()
221 .getAnnotatedMethods(AfterClass.class);
222 return afters.isEmpty() ? statement :
223 new RunAfters(statement, afters, null);
224 }
225
226
227
228
229
230
231
232
233
234
235 private Statement withClassRules(Statement statement) {
236 List<TestRule> classRules = classRules();
237 return classRules.isEmpty() ? statement :
238 new RunRules(statement, classRules, getDescription());
239 }
240
241
242
243
244
245 protected List<TestRule> classRules() {
246 List<TestRule> result = getTestClass().getAnnotatedMethodValues(null, ClassRule.class, TestRule.class);
247
248 result.addAll(getTestClass().getAnnotatedFieldValues(null, ClassRule.class, TestRule.class));
249
250 return result;
251 }
252
253
254
255
256
257
258 protected Statement childrenInvoker(final RunNotifier notifier) {
259 return new Statement() {
260 @Override
261 public void evaluate() {
262 runChildren(notifier);
263 }
264 };
265 }
266
267 private void runChildren(final RunNotifier notifier) {
268 for (final FrameworkMethod each : getFilteredChildren()) {
269 fScheduler.schedule(new Runnable() {
270 public void run() {
271 LoadTimeWeavableTestRunner.this.runChild(each, notifier);
272 }
273 });
274 }
275 fScheduler.finished();
276 }
277
278
279
280
281 protected String getName() {
282 return getOriginalTestClass().getName();
283 }
284
285
286
287
288 public final TestClass getTestClass() {
289 if (fTestClass == null) {
290 throw new IllegalStateException("Attempted to access test class but it has not yet been initialized!");
291 }
292 return fTestClass;
293 }
294
295
296
297
298 public final TestClass getOriginalTestClass() {
299 return originalTestClass;
300 }
301
302
303
304
305 protected final void runLeaf(Statement statement, Description description,
306 RunNotifier notifier) {
307 EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
308 eachNotifier.fireTestStarted();
309 try {
310 statement.evaluate();
311 } catch (AssumptionViolatedException e) {
312 eachNotifier.addFailedAssumption(e);
313 } catch (Throwable e) {
314 eachNotifier.addFailure(e);
315 } finally {
316 eachNotifier.fireTestFinished();
317 }
318 }
319
320
321
322
323
324 protected Annotation[] getRunnerAnnotations() {
325 return getOriginalTestClass().getAnnotations();
326 }
327
328
329
330
331
332 @Override
333 public Description getDescription() {
334 Description description = Description.createSuiteDescription(getName(),
335 getRunnerAnnotations());
336 for (FrameworkMethod child : getOriginalFilteredChildren()) {
337 description.addChild(describeOriginalChild(child));
338 }
339 return description;
340 }
341
342 @Override
343 public void run(final RunNotifier notifier) {
344 ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
345 Thread.currentThread().setContextClassLoader(customLoader);
346 try {
347 if (runBootstrapTest(notifier, getOriginalTestClass())) {
348 this.fTestClass = getCustomTestClass(getOriginalTestClass().getJavaClass(), customLoader);
349 EachTestNotifier testNotifier = new EachTestNotifier(notifier, getDescription());
350 try {
351 Statement statement = classBlock(notifier);
352 statement.evaluate();
353 } catch (AssumptionViolatedException e) {
354 testNotifier.fireTestIgnored();
355 } catch (StoppedByUserException e) {
356 throw e;
357 } catch (Throwable e) {
358 testNotifier.addFailure(e);
359 }
360 }
361 } finally {
362 Thread.currentThread().setContextClassLoader(currentContextClassLoader);
363 }
364 }
365
366 protected boolean runBootstrapTest(RunNotifier notifier, TestClass testClass) {
367 if (!runningBootstrapTest.get().booleanValue()) {
368 runningBootstrapTest.set(Boolean.TRUE);
369 try {
370 BootstrapTest bootstrapTest = getBootstrapTestAnnotation(testClass.getJavaClass());
371 if (bootstrapTest != null) {
372 Result result = JUnitCore.runClasses(bootstrapTest.value());
373 List<Failure> failures = result.getFailures();
374 for (Failure failure : failures) {
375 notifier.fireTestFailure(failure);
376 }
377 return result.getFailureCount() == 0;
378 } else {
379 throw new IllegalStateException("LoadTimeWeavableTestRunner, must be coupled with an @BootstrapTest annotation to define the bootstrap test to execute.");
380 }
381 } finally {
382 runningBootstrapTest.set(Boolean.FALSE);
383 }
384 }
385 return true;
386 }
387
388 private BootstrapTest getBootstrapTestAnnotation(Class<?> testClass) {
389 BootstrapTest bootstrapTest = testClass.getAnnotation(BootstrapTest.class);
390 if (bootstrapTest != null) {
391 return bootstrapTest;
392 } else if (testClass.getSuperclass() != null) {
393 return getBootstrapTestAnnotation(testClass.getSuperclass());
394 } else {
395 return null;
396 }
397 }
398
399
400
401
402
403 public void filter(Filter filter) throws NoTestsRemainException {
404 for (Iterator<FrameworkMethod> iter = getOriginalFilteredChildren().iterator(); iter.hasNext(); ) {
405 FrameworkMethod each = iter.next();
406 if (shouldRun(filter, each)) {
407 try {
408 filter.apply(each);
409 } catch (NoTestsRemainException e) {
410 iter.remove();
411 }
412 } else {
413 iter.remove();
414 }
415 }
416 if (getOriginalFilteredChildren().isEmpty()) {
417 throw new NoTestsRemainException();
418 }
419 }
420
421 public void sort(Sorter sorter) {
422 fSorter = sorter;
423 for (FrameworkMethod each : getOriginalFilteredChildren()) {
424 sortChild(each);
425 }
426 Collections.sort(getOriginalFilteredChildren(), comparator());
427 }
428
429
430
431
432
433 private void validate() throws InitializationError {
434 List<Throwable> errors = new ArrayList<Throwable>();
435 collectInitializationErrors(errors);
436 if (!errors.isEmpty()) {
437 throw new InitializationError(errors);
438 }
439 }
440
441 private List<FrameworkMethod> getOriginalFilteredChildren() {
442 if (originalFilteredChildren == null) {
443 originalFilteredChildren = new ArrayList<FrameworkMethod>(getOriginalChildren());
444 }
445 return originalFilteredChildren;
446 }
447
448 private List<FrameworkMethod> getFilteredChildren() {
449 if (getOriginalFilteredChildren() == null) {
450 throw new IllegalStateException("Attempted to get filtered children before original filtered children were initialized.");
451 }
452 if (filteredChildren == null) {
453 filteredChildren = new ArrayList<FrameworkMethod>();
454 List<FrameworkMethod> testMethods = computeTestMethods();
455 for (FrameworkMethod originalMethod : getOriginalFilteredChildren()) {
456 for (FrameworkMethod testMethod : testMethods) {
457 if (originalMethod.isShadowedBy(testMethod)) {
458 filteredChildren.add(testMethod);
459 }
460 }
461 }
462 }
463 return filteredChildren;
464 }
465
466 private void sortChild(FrameworkMethod child) {
467 fSorter.apply(child);
468 }
469
470 private boolean shouldRun(Filter filter, FrameworkMethod each) {
471 return filter.shouldRun(describeOriginalChild(each));
472 }
473
474 private Comparator<? super FrameworkMethod> comparator() {
475 return new Comparator<FrameworkMethod>() {
476 public int compare(FrameworkMethod o1, FrameworkMethod o2) {
477 return fSorter.compare(describeChild(o1), describeChild(o2));
478 }
479 };
480 }
481
482
483
484
485
486
487
488
489
490
491
492 protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
493 this.currentMethod = method.getMethod();
494 try {
495 Description description = describeChild(method);
496 if (method.getAnnotation(Ignore.class) != null) {
497 notifier.fireTestIgnored(description);
498 } else {
499 runLeaf(methodBlock(method), description, notifier);
500 }
501 } finally {
502 this.currentMethod = null;
503 }
504 }
505
506
507
508
509
510 protected Description describeChild(FrameworkMethod method) {
511 return Description.createTestDescription(getTestClass().getJavaClass(),
512 testName(method), method.getAnnotations());
513 }
514
515 protected Description describeOriginalChild(FrameworkMethod method) {
516 return Description.createTestDescription(getOriginalTestClass().getJavaClass(),
517 testName(method), method.getAnnotations());
518 }
519
520
521
522
523 protected List<FrameworkMethod> getChildren() {
524 return computeTestMethods();
525 }
526
527 protected List<FrameworkMethod> getOriginalChildren() {
528 return computeOriginalTestMethods();
529 }
530
531
532
533
534
535
536
537
538
539
540 protected List<FrameworkMethod> computeTestMethods() {
541 return getTestClass().getAnnotatedMethods(Test.class);
542 }
543
544 protected List<FrameworkMethod> computeOriginalTestMethods() {
545 return getOriginalTestClass().getAnnotatedMethods(Test.class);
546 }
547
548
549
550
551
552
553
554 protected void collectInitializationErrors(List<Throwable> errors) {
555 validatePublicVoidNoArgMethods(BeforeClass.class, true, errors);
556 validatePublicVoidNoArgMethods(AfterClass.class, true, errors);
557 validateClassRules(errors);
558 validateNoNonStaticInnerClass(errors);
559 validateConstructor(errors);
560 validateInstanceMethods(errors);
561 validateFields(errors);
562 validateMethods(errors);
563 }
564
565 protected void validateNoNonStaticInnerClass(List<Throwable> errors) {
566 if (getOriginalTestClass().isANonStaticInnerClass()) {
567 String gripe = "The inner class " + getOriginalTestClass().getName()
568 + " is not static.";
569 errors.add(new Exception(gripe));
570 }
571 }
572
573
574
575
576
577
578 protected void validateConstructor(List<Throwable> errors) {
579 validateOnlyOneConstructor(errors);
580 validateZeroArgConstructor(errors);
581 }
582
583
584
585
586
587 protected void validateOnlyOneConstructor(List<Throwable> errors) {
588 if (!hasOneConstructor()) {
589 String gripe = "Test class should have exactly one public constructor";
590 errors.add(new Exception(gripe));
591 }
592 }
593
594
595
596
597
598 protected void validateZeroArgConstructor(List<Throwable> errors) {
599 if (!getOriginalTestClass().isANonStaticInnerClass()
600 && hasOneConstructor()
601 && (getOriginalTestClass().getOnlyConstructor().getParameterTypes().length != 0)) {
602 String gripe = "Test class should have exactly one public zero-argument constructor";
603 errors.add(new Exception(gripe));
604 }
605 }
606
607 private boolean hasOneConstructor() {
608 return getOriginalTestClass().getJavaClass().getConstructors().length == 1;
609 }
610
611
612
613
614
615
616
617
618 @Deprecated
619 protected void validateInstanceMethods(List<Throwable> errors) {
620 validatePublicVoidNoArgMethods(After.class, false, errors);
621 validatePublicVoidNoArgMethods(Before.class, false, errors);
622 validateTestMethods(errors);
623
624 if (computeOriginalTestMethods().size() == 0) {
625 errors.add(new Exception("No runnable methods"));
626 }
627 }
628
629 protected void validateFields(List<Throwable> errors) {
630 RULE_VALIDATOR.validate(getOriginalTestClass(), errors);
631 }
632
633 private void validateMethods(List<Throwable> errors) {
634 RULE_METHOD_VALIDATOR.validate(getOriginalTestClass(), errors);
635 }
636
637
638
639
640
641 protected void validateTestMethods(List<Throwable> errors) {
642 validatePublicVoidNoArgMethods(Test.class, false, errors);
643 }
644
645
646
647
648
649
650 protected Object createTest() throws Exception {
651 Object test = getTestClass().getOnlyConstructor().newInstance();
652 setTestName(test, currentMethod);
653 setTestMethod(test, currentMethod);
654 return test;
655 }
656
657
658
659
660
661
662 protected void setTestMethod(Object test, Method method) throws Exception {
663 Class<?> methodAwareClass = Class.forName(MethodAware.class.getName(), true, getCustomClassLoader());
664 if (methodAwareClass.isInstance(test)) {
665 Method setTestMethod = methodAwareClass.getMethod("setTestMethod", Method.class);
666 setTestMethod.invoke(test, method);
667 }
668 }
669
670 protected void setTestName(final Object test, final Method testMethod) throws Exception {
671 String name = testMethod == null ? "" : testMethod.getName();
672 final Method setNameMethod = MethodUtils.getAccessibleMethod(test.getClass(), "setName",
673 new Class[]{String.class});
674 if (setNameMethod != null) {
675 setNameMethod.invoke(test, name);
676 }
677 }
678
679
680
681
682
683 protected String testName(FrameworkMethod method) {
684 return method.getName();
685 }
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719 protected Statement methodBlock(FrameworkMethod method) {
720 Object test;
721 try {
722 test = new ReflectiveCallable() {
723 @Override
724 protected Object runReflectiveCall() throws Throwable {
725 return createTest();
726 }
727 }.run();
728 } catch (Throwable e) {
729 return new Fail(e);
730 }
731
732 Statement statement = methodInvoker(method, test);
733 statement = possiblyExpectingExceptions(method, test, statement);
734 statement = withPotentialTimeout(method, test, statement);
735 statement = withBefores(method, test, statement);
736 statement = withAfters(method, test, statement);
737 statement = withRules(method, test, statement);
738 return statement;
739 }
740
741
742
743
744
745
746
747
748 protected Statement methodInvoker(FrameworkMethod method, Object test) {
749 return new InvokeMethod(method, test);
750 }
751
752
753
754
755
756
757
758
759
760 @Deprecated
761 protected Statement possiblyExpectingExceptions(FrameworkMethod method,
762 Object test, Statement next) {
763 Test annotation = method.getAnnotation(Test.class);
764 return expectsException(annotation) ? new ExpectException(next,
765 getExpectedException(annotation)) : next;
766 }
767
768
769
770
771
772
773
774
775 @Deprecated
776 protected Statement withPotentialTimeout(FrameworkMethod method,
777 Object test, Statement next) {
778 long timeout = getTimeout(method.getAnnotation(Test.class));
779 return timeout > 0 ? new FailOnTimeout(next, timeout) : next;
780 }
781
782
783
784
785
786
787
788
789 @Deprecated
790 protected Statement withBefores(FrameworkMethod method, Object target,
791 Statement statement) {
792 List<FrameworkMethod> befores = getTestClass().getAnnotatedMethods(Before.class);
793 return befores.isEmpty() ? statement : new RunBefores(statement,
794 befores, target);
795 }
796
797
798
799
800
801
802
803
804
805
806 @Deprecated
807 protected Statement withAfters(FrameworkMethod method, Object target,
808 Statement statement) {
809 List<FrameworkMethod> afters = getTestClass().getAnnotatedMethods(
810 After.class);
811 return afters.isEmpty() ? statement : new RunAfters(statement, afters,
812 target);
813 }
814
815 private Statement withRules(FrameworkMethod method, Object target,
816 Statement statement) {
817 List<TestRule> testRules = getTestRules(target);
818 Statement result = statement;
819 result = withMethodRules(method, testRules, target, result);
820 result = withTestRules(method, testRules, result);
821
822 return result;
823 }
824
825 private Statement withMethodRules(FrameworkMethod method, List<TestRule> testRules,
826 Object target, Statement result) {
827 for (org.junit.rules.MethodRule each : getMethodRules(target)) {
828 if (!testRules.contains(each)) {
829 result = each.apply(result, method, target);
830 }
831 }
832 return result;
833 }
834
835 private List<org.junit.rules.MethodRule> getMethodRules(Object target) {
836 return rules(target);
837 }
838
839
840
841
842
843
844 protected List<org.junit.rules.MethodRule> rules(Object target) {
845 return getTestClass().getAnnotatedFieldValues(target, Rule.class, org.junit.rules.MethodRule.class);
846 }
847
848
849
850
851
852
853
854
855
856 private Statement withTestRules(FrameworkMethod method, List<TestRule> testRules,
857 Statement statement) {
858 return testRules.isEmpty() ? statement :
859 new RunRules(statement, testRules, describeChild(method));
860 }
861
862
863
864
865
866
867 protected List<TestRule> getTestRules(Object target) {
868 List<TestRule> result = getTestClass().getAnnotatedMethodValues(target,
869 Rule.class, TestRule.class);
870
871 result.addAll(getTestClass().getAnnotatedFieldValues(target,
872 Rule.class, TestRule.class));
873
874 return result;
875 }
876
877 private Class<? extends Throwable> getExpectedException(Test annotation) {
878 if (annotation == null || annotation.expected() == Test.None.class) {
879 return null;
880 } else {
881 return annotation.expected();
882 }
883 }
884
885 private boolean expectsException(Test annotation) {
886 return getExpectedException(annotation) != null;
887 }
888
889 private long getTimeout(Test annotation) {
890 if (annotation == null) {
891 return 0;
892 }
893 return annotation.timeout();
894 }
895
896 }