3939import java .io .IOException ;
4040import java .io .InputStream ;
4141import java .io .InputStreamReader ;
42- import java .lang .reflect .Field ;
4342import java .lang .reflect .InvocationTargetException ;
4443import java .lang .reflect .Method ;
4544import java .net .URI ;
@@ -67,6 +66,12 @@ public class TestChooserCli {
6766
6867 private static final Logger logger = Logger .getLogger (TestChooserCli .class .getName ());
6968 private static final String CLASS_LIST_RESOURCE = "/jme3test/test-classes.txt" ;
69+ private static final long WAIT_INTERVAL_MILLIS = Math .max (10L ,
70+ Long .getLong ("jme3test.cli.waitIntervalMillis" , 100L ));
71+ private static final long START_TIMEOUT_MILLIS = Math .max (WAIT_INTERVAL_MILLIS ,
72+ Long .getLong ("jme3test.cli.startTimeoutMillis" , 30000L ));
73+ private static final long RUN_TIMEOUT_MILLIS = Long .getLong ("jme3test.cli.runTimeoutMillis" ,
74+ 30L * 60L * 1000L );
7075
7176 public static void main (String [] args ) {
7277 new TestChooserCli ().start (args );
@@ -212,27 +217,15 @@ private String chooseClassName(List<String> classNames) {
212217 private void launchClass (Class <?> clazz , String [] appArgs ) {
213218 try {
214219 if (LegacyApplication .class .isAssignableFrom (clazz )) {
215- Object app = clazz .getDeclaredConstructor ().newInstance ();
220+ LegacyApplication app = ( LegacyApplication ) clazz .getDeclaredConstructor ().newInstance ();
216221 if (app instanceof SimpleApplication ) {
217- Method settingMethod = clazz .getMethod ("setShowSettings" , boolean .class );
218- settingMethod .invoke (app , false );
219- }
220- Method startMethod = clazz .getMethod ("start" );
221- startMethod .invoke (app );
222-
223- Field contextField = LegacyApplication .class .getDeclaredField ("context" );
224- contextField .setAccessible (true );
225- JmeContext context = null ;
226- while (context == null ) {
227- context = (JmeContext ) contextField .get (app );
228- Thread .sleep (100 );
229- }
230- while (!context .isCreated ()) {
231- Thread .sleep (100 );
232- }
233- while (context .isCreated ()) {
234- Thread .sleep (100 );
222+ ((SimpleApplication ) app ).setShowSettings (false );
235223 }
224+ app .start ();
225+
226+ JmeContext context = waitForContext (app , clazz .getName ());
227+ waitForContextCreated (app , context , clazz .getName ());
228+ waitForContextDestroyed (app , context , clazz .getName ());
236229 } else {
237230 Method mainMethod = clazz .getMethod ("main" , String [].class );
238231 mainMethod .invoke (null , new Object []{appArgs });
@@ -247,8 +240,6 @@ private void launchClass(Class<?> clazz, String[] appArgs) {
247240 logger .log (Level .SEVERE , "Failed to create app: " + clazz .getName (), e );
248241 } catch (NoSuchMethodException e ) {
249242 logger .log (Level .SEVERE , "Test class does not have required method: " + clazz .getName (), e );
250- } catch (NoSuchFieldException e ) {
251- logger .log (Level .SEVERE , "Cannot access application context: " + clazz .getName (), e );
252243 } catch (InterruptedException e ) {
253244 Thread .currentThread ().interrupt ();
254245 logger .log (Level .SEVERE , "Interrupted while waiting for app context" , e );
@@ -257,6 +248,56 @@ private void launchClass(Class<?> clazz, String[] appArgs) {
257248 }
258249 }
259250
251+ private JmeContext waitForContext (LegacyApplication app , String className ) throws InterruptedException {
252+ long deadline = System .currentTimeMillis () + START_TIMEOUT_MILLIS ;
253+ JmeContext context = app .getContext ();
254+ while (context == null ) {
255+ if (System .currentTimeMillis () >= deadline ) {
256+ requestStop (app );
257+ throw new IllegalStateException ("Timed out waiting for application context: " + className );
258+ }
259+ Thread .sleep (WAIT_INTERVAL_MILLIS );
260+ context = app .getContext ();
261+ }
262+ return context ;
263+ }
264+
265+ private void waitForContextCreated (LegacyApplication app , JmeContext context , String className )
266+ throws InterruptedException {
267+ long deadline = System .currentTimeMillis () + START_TIMEOUT_MILLIS ;
268+ while (!context .isCreated ()) {
269+ if (System .currentTimeMillis () >= deadline ) {
270+ requestStop (app );
271+ throw new IllegalStateException ("Timed out waiting for context creation: " + className );
272+ }
273+ Thread .sleep (WAIT_INTERVAL_MILLIS );
274+ }
275+ }
276+
277+ private void waitForContextDestroyed (LegacyApplication app , JmeContext context , String className )
278+ throws InterruptedException {
279+ long deadline = RUN_TIMEOUT_MILLIS > 0L ? System .currentTimeMillis () + RUN_TIMEOUT_MILLIS : Long .MAX_VALUE ;
280+ while (context .isCreated ()) {
281+ if (System .currentTimeMillis () >= deadline ) {
282+ requestStop (app );
283+ throw new IllegalStateException ("Timed out waiting for application to exit: " + className );
284+ }
285+ Thread .sleep (WAIT_INTERVAL_MILLIS );
286+ }
287+ }
288+
289+ private void requestStop (LegacyApplication app ) {
290+ JmeContext context = app .getContext ();
291+ if (context == null ) {
292+ return ;
293+ }
294+ try {
295+ app .stop (false );
296+ } catch (RuntimeException e ) {
297+ logger .log (Level .WARNING , "Failed to stop timed-out application" , e );
298+ }
299+ }
300+
260301 private void find (String packageName , boolean recursive , Set <Class <?>> classes ) {
261302 String name = packageName ;
262303 if (!name .startsWith ("/" )) {
0 commit comments