33import java .lang .reflect .InvocationTargetException ;
44import java .util .Arrays ;
55import java .util .List ;
6+ import java .util .concurrent .atomic .AtomicBoolean ;
67
78import static engine .Texts .*;
89
910/**
1011 * The main engine class, executing the series of koans.
1112 */
1213public class Sensei {
14+ private static final long TIMEOUT_INFINITE_LOOPS_MS = 2000 ;
1315 private final Locale locale ;
1416 private final Printer consolePrinter ;
1517 private Printer p = Printer .SILENT ;
@@ -55,7 +57,8 @@ private boolean tryOffer(KoanTest test, int successfulCount) {
5557 if (!succeeded ) {
5658 // If failed, execute verbosely the second time, in order to give feedback to the student.
5759 p = consolePrinter ;
58- return offer (test , successfulCount );
60+ offer (test , successfulCount );
61+ return false ;
5962 }
6063
6164 return true ;
@@ -66,53 +69,75 @@ private boolean offer(KoanTest test, int successfulCount) {
6669 observe (koan );
6770 encourage ();
6871
69- var success = false ;
72+ AtomicBoolean success = new AtomicBoolean (false );
73+
74+ var thread = new Thread (() -> {
75+ try {
76+ success .set (executeCall (test ));
77+ } catch (IllegalAccessException iae ) {
78+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
79+ concludeConsole (koan );
80+ p .println (Color .red (EXPECTED_METHOD_TO_BE_PUBLIC ), koan .methodName );
81+ } catch (IllegalArgumentException iae ) {
82+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
83+ concludeConsole (koan );
84+ // Would be a bug in the Koan instances, since we are ensuring for the method with the right parameters.
85+ p .println (Color .red (THE_METHOD_APPEARS_TO_PRODUCE_AN_ERROR ), koan .methodName , iae .getMessage ());
86+ } catch (InvocationTargetException ite ) {
87+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
88+ concludeConsole (koan );
89+ if (ite .getTargetException () instanceof StackOverflowError ) {
90+ p .println (Color .red (THE_METHOD_SEEMS_TO_RECURSE_INFINITELY ), koan .methodName );
91+ } else {
92+ p .println (Color .red (THE_METHOD_APPEARS_TO_PRODUCE_AN_ERROR ), koan .methodName , ite .getCause ().getMessage ());
93+ }
94+ } catch (NoStaticMethodException nsme ) {
95+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
96+ concludeConsole (koan );
97+ p .println (Color .red (EXPECTED_METHOD_TO_BE_STATIC ), koan .methodName , koan .exerciseClassName (locale ).replace ("." , "/" ));
98+ } catch (NoDynamicMethodException ndme ) {
99+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
100+ concludeConsole (koan );
101+ p .println (Color .red (EXPECTED_METHOD_TO_NOT_BE_STATIC ), koan .methodName , koan .exerciseClassName (locale ));
102+ } catch (NoSuchConstructorException nsce ) {
103+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
104+ concludeConsole (koan );
105+ displayConstructorNotFound (koan );
106+ } catch (NoSuchMethodException mnfe ) {
107+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
108+ concludeConsole (koan );
109+ displayMethodNotFound (koan );
110+ } catch (ClassNotFoundException cnfe ) {
111+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
112+ concludeConsole (koan );
113+ p .println (Color .red (EXPECTED_TO_FIND_A_CLASS_IN_THE_PACKAGE ), koan .exerciseClassName .get (), koan .exerciseClassPackage .get ());
114+ } catch (InstantiationException ie ) {
115+ // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
116+ concludeConsole (koan );
117+ // Would be a bug in the Koan instances, since we are ensuring for the method with the right parameters.
118+ p .println (Color .red (THE_CONSTRUCTOR_APPEARS_TO_PRODUCE_AN_ERROR ), koan .exerciseClassName .get ());
119+ }
120+ });
121+
122+ thread .setDaemon (true );
123+ thread .start ();
70124 try {
71- success = executeCall (test );
72- } catch (IllegalAccessException iae ) {
73- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
74- concludeConsole (koan );
75- p .println (Color .red (EXPECTED_METHOD_TO_BE_PUBLIC ), koan .methodName );
76- } catch (IllegalArgumentException iae ) {
77- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
78- concludeConsole (koan );
79- // Would be a bug in the Koan instances, since we are ensuring for the method with the right parameters.
80- p .println (Color .red (THE_METHOD_APPEARS_TO_PRODUCE_AN_ERROR ), koan .methodName , iae .getMessage ());
81- } catch (InvocationTargetException ite ) {
82- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
83- concludeConsole (koan );
84- p .println (Color .red (THE_METHOD_APPEARS_TO_PRODUCE_AN_ERROR ), koan .methodName , ite .getCause ().getMessage ());
85- } catch (NoStaticMethodException nsme ) {
86- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
87- concludeConsole (koan );
88- p .println (Color .red (EXPECTED_METHOD_TO_BE_STATIC ), koan .methodName , koan .exerciseClassName (locale ).replace ("." , "/" ));
89- } catch (NoDynamicMethodException ndme ) {
90- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
91- concludeConsole (koan );
92- p .println (Color .red (EXPECTED_METHOD_TO_NOT_BE_STATIC ), koan .methodName , koan .exerciseClassName (locale ));
93- } catch (NoSuchConstructorException nsce ) {
94- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
95- concludeConsole (koan );
96- displayConstructorNotFound (koan );
97- } catch (NoSuchMethodException mnfe ) {
98- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
99- concludeConsole (koan );
100- displayMethodNotFound (koan );
101- } catch (ClassNotFoundException cnfe ) {
102- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
103- concludeConsole (koan );
104- p .println (Color .red (EXPECTED_TO_FIND_A_CLASS_IN_THE_PACKAGE ), koan .exerciseClassName .get (), koan .exerciseClassPackage .get ());
105- } catch (InstantiationException ie ) {
106- // Special case: since the executeCall() method did not complete, the console conclusion was not displayed.
125+ thread .join (TIMEOUT_INFINITE_LOOPS_MS );
126+ }
127+ catch (InterruptedException ie ) {
128+ throw new IllegalStateException ("Something very weird happened. We should not have been interrupted." );
129+ }
130+
131+ if (thread .isAlive ()) {
132+ StdStreamsInterceptor .reset ();
107133 concludeConsole (koan );
108- // Would be a bug in the Koan instances, since we are ensuring for the method with the right parameters.
109- p .println (Color .red (THE_CONSTRUCTOR_APPEARS_TO_PRODUCE_AN_ERROR ), koan .exerciseClassName .get ());
134+ p .println (Color .red (THE_METHOD_SEEMS_TO_NOT_FINISH ), koan .methodName );
110135 }
111136
112137 offerToMeditate (koan );
113138 showProgress (successfulCount );
114139
115- return success ;
140+ return success . get () ;
116141 }
117142
118143 private boolean executeCall (KoanTest test ) throws NoSuchMethodException , IllegalAccessException , InvocationTargetException , ClassNotFoundException , InstantiationException {
0 commit comments