1 /*
2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 import java.awt.*;
25 import java.awt.event.*;
26 import java.awt.peer.ComponentPeer;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30 import java.util.ArrayList;
31 import javax.swing.*;
32 
33 import sun.awt.AWTAccessor;
34 import sun.awt.EmbeddedFrame;
35 import java.io.*;
36 import test.java.awt.regtesthelpers.Util;
37 
38 /**
39  * <p>This class provides basis for AWT Mixing testing.
40  * <p>It provides all standard test machinery and should be used by
41  * extending and overriding next methods:
42  * <li> {@link OverlappingTestBase#prepareControls()} - setup UI components
43  * <li> {@link OverlappingTestBase#performTest()} -  run particular test
44  * Those methods would be run in the loop for each AWT component.
45  * <p>Current AWT component should be added to the tested UI by {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) ()}.
46  * There AWT components are prepared to be tested as being overlayed by other (e.g. Swing) components - they are colored to
47  * {@link OverlappingTestBase#AWT_BACKGROUND_COLOR} and throws failure on catching mouse event.
48  * <p> Validation of component being overlayed should be tested by {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point) }
49  * See each method javadoc for more details.
50  *
51  * <p>Due to test machinery limitations all test should be run from their own main() by calling next coe
52  * <code>
53  *     public static void main(String args[]) throws InterruptedException {
54  *        instance = new YourTestInstance();
55  *        OverlappingTestBase.doMain(args);
56  *     }
57  * </code>
58  *
59  * @author Sergey Grinev
60  */
61 public abstract class OverlappingTestBase {
62     // working variables
63     private static volatile boolean wasHWClicked = false;
64     private static volatile boolean passed = true;
65     // constants
66     /**
67      * Default color for AWT component used for validate correct drawing of overlapping. <b>Never</b> use it for lightweight components.
68      */
69     protected static final Color AWT_BACKGROUND_COLOR = new Color(21, 244, 54);
70     protected static Color AWT_VERIFY_COLOR = AWT_BACKGROUND_COLOR;
71     protected static final int ROBOT_DELAY = 500;
72     private static final String[] simpleAwtControls = {"Button", "Checkbox", "Label", "TextArea"};
73     /**
74      * Generic strings array. To be used for population of List based controls.
75      */
76     protected static final String[] petStrings = {"Bird", "Cat", "Dog", "Rabbit", "Rhynocephalia Granda", "Bear", "Tiger", "Mustang"};
77     // "properties"
78     /**
79      * Tests customization. Set this variable to test only control from java.awt
80      * <p>Usage of this variable should be marked with CR being the reason.
81      * <p>Do not use this variable simultaneously with {@link OverlappingTestBase#skipClassNames}
82      */
83     protected String onlyClassName = null;
84     /**
85      * For customizing tests. List classes' simple names to skip them from testings.
86      * <p>Usage of this variable should be marked with CR being the reason.
87      */
88     protected String[] skipClassNames = null;
89     /**
90      * Set to false to avoid event delivery validation
91      * @see OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean)
92      */
93     protected boolean useClickValidation = true;
94     /**
95      * Set to false if test doesn't supposed to verify EmbeddedFrame
96      */
97     protected boolean testEmbeddedFrame = false;
98     /**
99      * Set this variable to true if testing embedded frame is impossible (on Mac, for instance, for now).
100      * The testEmbeddedFrame is explicitly set to true in dozen places.
101      */
102     protected boolean skipTestingEmbeddedFrame = false;
103 
104     public static final boolean isMac = System.getProperty("os.name").toLowerCase().contains("os x");
105     private boolean isFrameBorderCalculated;
106     private int borderShift;
107 
108     {    if (Toolkit.getDefaultToolkit().getClass().getName().matches(".*L.*Toolkit")) {
109              // No EmbeddedFrame in LWToolkit/LWCToolkit, yet
110              // And it should be programmed some other way, too, in any case
111              //System.err.println("skipTestingEmbeddedFrame");
112              //skipTestingEmbeddedFrame = true;
113          }else {
114              System.err.println("do not skipTestingEmbeddedFrame");
115          }
116     }
117 
frameBorderCounter()118     protected int frameBorderCounter() {
119         if (!isFrameBorderCalculated) {
120             try {
121                 new FrameBorderCounter(); // force compilation by jtreg
122                 String JAVA_HOME = System.getProperty("java.home");
123                 Process p = Runtime.getRuntime().exec(JAVA_HOME + "/bin/java FrameBorderCounter");
124                 try {
125                     p.waitFor();
126                 } catch (InterruptedException e) {
127                     e.printStackTrace();
128                     throw new RuntimeException(e);
129                 }
130                 if (p.exitValue() != 0) {
131                     throw new RuntimeException("FrameBorderCounter exited with not null code!\n" + readInputStream(p.getErrorStream()));
132                 }
133                 borderShift = Integer.parseInt(readInputStream(p.getInputStream()).trim());
134                 isFrameBorderCalculated = true;
135             } catch (IOException e) {
136                 e.printStackTrace();
137                 throw new RuntimeException("Problem calculating a native border size");
138             }
139         }
140         return borderShift;
141     }
142 
getVerifyColor()143     public void getVerifyColor() {
144         try {
145             final int size = 200;
146             final Point[] p = new Point[1];
147             SwingUtilities.invokeAndWait(new Runnable() {
148                 public void run(){
149                     JFrame frame = new JFrame("set back");
150                     frame.getContentPane().setBackground(AWT_BACKGROUND_COLOR);
151                     frame.setSize(size, size);
152                     frame.setUndecorated(true);
153                     frame.setVisible(true);
154                     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
155                     p[0] = frame.getLocation();
156                 }
157             });
158             Robot robot = new Robot();
159             robot.waitForIdle();
160             Thread.sleep(ROBOT_DELAY);
161             AWT_VERIFY_COLOR = robot.getPixelColor(p[0].x+size/2, p[0].y+size/2);
162             System.out.println("Color will be compared with " + AWT_VERIFY_COLOR + " instead of " + AWT_BACKGROUND_COLOR);
163         } catch (Exception e) {
164             System.err.println("Cannot get verify color: "+e.getMessage());
165         }
166     }
167 
readInputStream(InputStream is)168     private String readInputStream(InputStream is) throws IOException {
169         byte[] buffer = new byte[4096];
170         int len = 0;
171         StringBuilder sb = new StringBuilder();
172         try (InputStreamReader isr = new InputStreamReader(is)) {
173             while ((len = is.read(buffer)) > 0) {
174                 sb.append(new String(buffer, 0, len));
175             }
176         }
177         return sb.toString();
178     }
179 
setupControl(final Component control)180     private void setupControl(final Component control) {
181         if (useClickValidation) {
182             control.addMouseListener(new MouseAdapter() {
183                 @Override
184                 public void mouseClicked(MouseEvent e) {
185                     System.err.println("ERROR: " + control.getClass() + " received mouse click.");
186                     wasHWClicked = true;
187                 }
188             });
189         }
190         control.setBackground(AWT_BACKGROUND_COLOR);
191         control.setForeground(AWT_BACKGROUND_COLOR);
192         control.setPreferredSize(new Dimension(150, 150));
193         control.setFocusable(false);
194     }
195 
addAwtControl(java.util.List<Component> container, final Component control)196     private void addAwtControl(java.util.List<Component> container, final Component control) {
197         String simpleName = control.getClass().getSimpleName();
198         if (onlyClassName != null && !simpleName.equals(onlyClassName)) {
199             return;
200         }
201         if (skipClassNames != null) {
202             for (String skipMe : skipClassNames) {
203                 if (simpleName.equals(skipMe)) {
204                     return;
205                 }
206             }
207         }
208         setupControl(control);
209         container.add(control);
210     }
211 
addSimpleAwtControl(java.util.List<Component> container, String className)212     private void addSimpleAwtControl(java.util.List<Component> container, String className) {
213         try {
214             Class definition = Class.forName("java.awt." + className);
215             Constructor constructor = definition.getConstructor(new Class[]{String.class});
216             java.awt.Component component = (java.awt.Component) constructor.newInstance(new Object[]{"AWT Component " + className});
217             addAwtControl(container, component);
218         } catch (Exception ex) {
219             System.err.println(ex.getMessage());
220             fail("Setup error, this jdk doesn't have awt conrol " + className);
221         }
222     }
223 
224     /**
225      * Adds current AWT control to container
226      * <p>N.B.: if testEmbeddedFrame == true this method will also add EmbeddedFrame over Canvas
227      * and it should be called <b>after</b> Frame.setVisible(true) call
228      * @param container container to hold AWT component
229      */
propagateAWTControls(Container container)230     protected final void propagateAWTControls(Container container) {
231         if (currentAwtControl != null) {
232             container.add(currentAwtControl);
233         } else { // embedded frame
234             try {
235 
236                 //create embedder
237                 Canvas embedder = new Canvas();
238                 embedder.setBackground(Color.RED);
239                 embedder.setPreferredSize(new Dimension(150, 150));
240                 container.add(embedder);
241                 container.setVisible(true); // create peer
242 
243                 long frameWindow = 0;
244                 String getWindowMethodName = null;
245                 String eframeClassName = null;
246                 if (Toolkit.getDefaultToolkit().getClass().getName().contains("XToolkit")) {
247                     java.awt.Helper.addExports("sun.awt.X11", OverlappingTestBase.class.getModule());
248                     getWindowMethodName = "getWindow";
249                     eframeClassName = "sun.awt.X11.XEmbeddedFrame";
250                 }else if (Toolkit.getDefaultToolkit().getClass().getName().contains(".WToolkit")) {
251                     java.awt.Helper.addExports("sun.awt.windows", OverlappingTestBase.class.getModule());
252                     getWindowMethodName = "getHWnd";
253                     eframeClassName = "sun.awt.windows.WEmbeddedFrame";
254                 }else if (isMac) {
255                     java.awt.Helper.addExports("sun.lwawt", OverlappingTestBase.class.getModule());
256                     java.awt.Helper.addExports("sun.lwawt.macosx", OverlappingTestBase.class.getModule());
257                     eframeClassName = "sun.lwawt.macosx.CViewEmbeddedFrame";
258                 }
259 
260                 ComponentPeer peer = AWTAccessor.getComponentAccessor()
261                                                 .getPeer(embedder);
262                 if (!isMac) {
263                     Method getWindowMethod = peer.getClass().getMethod(getWindowMethodName);
264                     frameWindow = (Long) getWindowMethod.invoke(peer);
265                 } else {
266                     Method m_getPlatformWindowMethod = peer.getClass().getMethod("getPlatformWindow");
267                     Object platformWindow = m_getPlatformWindowMethod.invoke(peer);
268                     Class classPlatformWindow = Class.forName("sun.lwawt.macosx.CPlatformWindow");
269 
270                     Method m_getContentView = classPlatformWindow.getMethod("getContentView");
271                     Object contentView = m_getContentView.invoke(platformWindow);
272                     Class classContentView = Class.forName("sun.lwawt.macosx.CPlatformView");
273 
274                     Method m_getAWTView = classContentView.getMethod("getAWTView");
275                     frameWindow = (Long) m_getAWTView.invoke(contentView);
276                 }
277 
278                 Class eframeClass = Class.forName(eframeClassName);
279                 Constructor eframeCtor = eframeClass.getConstructor(long.class);
280                 EmbeddedFrame eframe = (EmbeddedFrame) eframeCtor.newInstance(frameWindow);
281                 setupControl(eframe);
282                 eframe.setSize(new Dimension(150, 150));
283                 eframe.setVisible(true);
284 //                System.err.println(eframe.getSize());
285             } catch (Exception ex) {
286                 ex.printStackTrace();
287                 fail("Failed to instantiate EmbeddedFrame: " + ex.getMessage());
288             }
289         }
290     }
291     private static final Font hugeFont = new Font("Arial", Font.BOLD, 70);
292 
getAWTControls()293     private java.util.List<Component> getAWTControls() {
294         java.util.List<Component> components = new ArrayList<Component>();
295 
296         for (String clazz : simpleAwtControls) {
297             addSimpleAwtControl(components, clazz);
298         }
299 
300         TextField tf = new TextField();
301         tf.setFont(hugeFont);
302         addAwtControl(components, tf);
303 
304         // more complex controls
305         Choice c = new Choice();
306         for (int i = 0; i < petStrings.length; i++) {
307             c.add(petStrings[i]);
308         }
309         addAwtControl(components, c);
310         c.setPreferredSize(null);
311         c.setFont(hugeFont); // to make control bigger as setPrefferedSize don't do his job here
312 
313         List l = new List(petStrings.length);
314         for (int i = 0; i < petStrings.length; i++) {
315             l.add(petStrings[i]);
316         }
317         addAwtControl(components, l);
318 
319         Canvas canvas = new Canvas();
320         canvas.setSize(100, 200);
321         addAwtControl(components, canvas);
322 
323         Scrollbar sb = new Scrollbar(Scrollbar.VERTICAL, 500, 1, 0, 500);
324         addAwtControl(components, sb);
325 
326         Scrollbar sb2 = new Scrollbar(Scrollbar.HORIZONTAL, 500, 1, 0, 500);
327         addAwtControl(components, sb2);
328 
329         return components;
330     }
331     /**
332      * Default shift for {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point) }
333      */
334     protected static Point shift = new Point(16, 16);
335 
336     /**
337      * Verifies point using specified AWT Robot. Supposes <code>defaultShift == true</code> for {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean) }.
338      * This method is used to verify controls by providing just their plain screen coordinates.
339      * @param robot AWT Robot. Usually created by {@link Util#createRobot() }
340      * @param lLoc point to verify
341      * @see OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean)
342      */
clickAndBlink(Robot robot, Point lLoc)343     protected void clickAndBlink(Robot robot, Point lLoc) {
344         clickAndBlink(robot, lLoc, true);
345     }
346     /**
347      * Default failure message for color check
348      * @see OverlappingTestBase#performTest()
349      */
350     protected String failMessageColorCheck = "The LW component did not pass pixel color check and is overlapped";
351     /**
352      * Default failure message event check
353      * @see OverlappingTestBase#performTest()
354      */
355     protected String failMessage = "The LW component did not received the click.";
356 
isValidForPixelCheck(Component component)357     private static boolean isValidForPixelCheck(Component component) {
358         if ((component instanceof java.awt.Scrollbar) || isMac && (component instanceof java.awt.Button)) {
359             return false;
360         }
361         return true;
362     }
363 
364     /**
365      * Preliminary validation - should be run <b>before</b> overlapping happens to ensure test is correct.
366      * @param robot AWT Robot. Usually created by {@link Util#createRobot() }
367      * @param lLoc point to validate to be <b>of</b> {@link OverlappingTestBase#AWT_BACKGROUND_COLOR}
368      * @param component tested component, should be pointed out as not all components are valid for pixel check.
369      */
pixelPreCheck(Robot robot, Point lLoc, Component component)370     protected void pixelPreCheck(Robot robot, Point lLoc, Component component) {
371         if (isValidForPixelCheck(component)) {
372             int tries = 10;
373             Color c = null;
374             while (tries-- > 0) {
375                 c = robot.getPixelColor(lLoc.x, lLoc.y);
376                 System.out.println("Precheck. color: "+c+" compare with "+AWT_VERIFY_COLOR);
377                 if (c.equals(AWT_VERIFY_COLOR)) {
378                     return;
379                 }
380                 try {
381                     Thread.sleep(100);
382                 } catch (InterruptedException e) {
383                 }
384             }
385             System.err.println(lLoc + ": " + c);
386             fail("Dropdown test setup failure, colored part of AWT component is not located at click area");
387         }
388     }
389 
390     /**
391      * Verifies point using specified AWT Robot.
392      * <p>Firstly, verifies point by color pixel check
393      * <p>Secondly, verifies event delivery by mouse click
394      * @param robot AWT Robot. Usually created by {@link Util#createRobot() }
395      * @param lLoc point to verify
396      * @param defaultShift if true verified position will be shifted by {@link OverlappingTestBase#shift }.
397      */
clickAndBlink(Robot robot, Point lLoc, boolean defaultShift)398     protected void clickAndBlink(Robot robot, Point lLoc, boolean defaultShift) {
399         Point loc = lLoc.getLocation();
400         //check color
401         Util.waitForIdle(robot);
402         try{
403             Thread.sleep(500);
404         }catch(Exception exx){
405             exx.printStackTrace();
406         }
407 
408         if (defaultShift) {
409             loc.translate(shift.x, shift.y);
410         }
411         if (!(System.getProperty("os.name").toLowerCase().contains("os x"))) {
412             Color c = robot.getPixelColor(loc.x, loc.y);
413             System.out.println("C&B. color: "+c+" compare with "+AWT_VERIFY_COLOR);
414             if (c.equals(AWT_VERIFY_COLOR)) {
415                 fail(failMessageColorCheck);
416                 passed = false;
417             }
418 
419             // perform click
420             Util.waitForIdle(robot);
421         }
422 
423         robot.mouseMove(loc.x, loc.y);
424 
425         robot.mousePress(InputEvent.BUTTON1_MASK);
426         robot.mouseRelease(InputEvent.BUTTON1_MASK);
427         Util.waitForIdle(robot);
428     }
429 
430     /**
431      * This method should be overriden with code which setups UI for testing.
432      * Code in this method <b>will</b> be called only from AWT thread so Swing operations can be called directly.
433      *
434      * @see {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) } for instructions about adding tested AWT control to UI
435      */
prepareControls()436     protected abstract void prepareControls();
437 
438     /**
439      * This method should be overriden with test execution. It will <b>not</b> be called from AWT thread so all Swing operations should be treated accordingly.
440      * @return true if test passed. Otherwise fail with default fail message.
441      * @see {@link OverlappingTestBase#failMessage} default fail message
442      */
performTest()443     protected abstract boolean performTest();
444     /**
445      * This method can be overriden with cleanup routines. It will be called from AWT thread so all Swing operations should be treated accordingly.
446      */
cleanup()447     protected void cleanup() {
448         // intentionally do nothing
449     }
450     /**
451      * Currect tested AWT Control. Usually shouldn't be accessed directly.
452      *
453      * @see {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) } for instructions about adding tested AWT control to UI
454      */
455     protected Component currentAwtControl;
456 
testComponent(Component component)457     private void testComponent(Component component) throws InterruptedException, InvocationTargetException {
458         Robot robot = null;
459         try {
460             robot = new Robot();
461         }catch(Exception ignorex) {
462         }
463         currentAwtControl = component;
464         System.out.println("Testing " + currentAwtControl.getClass().getSimpleName());
465         SwingUtilities.invokeAndWait(new Runnable() {
466             public void run() {
467                 prepareControls();
468             }
469         });
470         if (component != null) {
471             Util.waitTillShown(component);
472         }
473         Util.waitForIdle(robot);
474         try {
475             Thread.sleep(500); // wait for graphic effects on systems like Win7
476         } catch (InterruptedException ex) {
477         }
478         if (!instance.performTest()) {
479             fail(failMessage);
480             passed = false;
481         }
482         SwingUtilities.invokeAndWait(new Runnable() {
483             public void run() {
484                 cleanup();
485             }
486         });
487     }
488 
testEmbeddedFrame()489     private void testEmbeddedFrame() throws InvocationTargetException, InterruptedException {
490         Robot robot = null;
491         try {
492             robot = new Robot();
493         }catch(Exception ignorex) {
494         }
495         System.out.println("Testing EmbeddedFrame");
496         currentAwtControl = null;
497         SwingUtilities.invokeAndWait(new Runnable() {
498             public void run() {
499                 prepareControls();
500             }
501         });
502         Util.waitForIdle(robot);
503         try {
504             Thread.sleep(500); // wait for graphic effects on systems like Win7
505         } catch (InterruptedException ex) {
506         }
507         if (!instance.performTest()) {
508             fail(failMessage);
509             passed = false;
510         }
511         SwingUtilities.invokeAndWait(new Runnable() {
512             public void run() {
513                 cleanup();
514             }
515         });
516     }
517 
testAwtControls()518     private void testAwtControls() throws InterruptedException {
519         try {
520             for (Component component : getAWTControls()) {
521                 testComponent(component);
522             }
523             if (testEmbeddedFrame && !skipTestingEmbeddedFrame) {
524                 testEmbeddedFrame();
525             }
526         } catch (InvocationTargetException ex) {
527             ex.printStackTrace();
528             fail(ex.getMessage());
529         }
530     }
531     /**
532      * Used by standard test machinery. See usage at {@link OverlappingTestBase }
533      */
534     protected static OverlappingTestBase instance;
535 
OverlappingTestBase()536     protected OverlappingTestBase() {
537         getVerifyColor();
538     }
539 
540     /*****************************************************
541      * Standard Test Machinery Section
542      * DO NOT modify anything in this section -- it's a
543      * standard chunk of code which has all of the
544      * synchronisation necessary for the test harness.
545      * By keeping it the same in all tests, it is easier
546      * to read and understand someone else's test, as
547      * well as insuring that all tests behave correctly
548      * with the test harness.
549      * There is a section following this for test-
550      * classes
551      ******************************************************/
init()552     private static void init() throws InterruptedException {
553         //System.setProperty("sun.awt.disableMixing", "true");
554 
555         instance.testAwtControls();
556 
557         if (wasHWClicked) {
558             fail("HW component received the click.");
559             passed = false;
560         }
561         if (passed) {
562             pass();
563         }
564     }//End  init()
565     private static boolean theTestPassed = false;
566     private static boolean testGeneratedInterrupt = false;
567     private static String failureMessage = "";
568     private static Thread mainThread = null;
569     private static int sleepTime = 300000;
570 
571     // Not sure about what happens if multiple of this test are
572     //  instantiated in the same VM.  Being static (and using
573     //  static vars), it aint gonna work.  Not worrying about
574     //  it for now.
575     /**
576      * Starting point for test runs. See usage at {@link OverlappingTestBase }
577      * @param args regular main args, not used.
578      * @throws InterruptedException
579      */
doMain(String args[])580     public static void doMain(String args[]) throws InterruptedException {
581         mainThread = Thread.currentThread();
582         try {
583             init();
584         } catch (TestPassedException e) {
585             //The test passed, so just return from main and harness will
586             // interepret this return as a pass
587             return;
588         }
589         //At this point, neither test pass nor test fail has been
590         // called -- either would have thrown an exception and ended the
591         // test, so we know we have multiple threads.
592 
593         //Test involves other threads, so sleep and wait for them to
594         // called pass() or fail()
595         try {
596             Thread.sleep(sleepTime);
597             //Timed out, so fail the test
598             throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds");
599         } catch (InterruptedException e) {
600             //The test harness may have interrupted the test.  If so, rethrow the exception
601             // so that the harness gets it and deals with it.
602             if (!testGeneratedInterrupt) {
603                 throw e;
604             }
605 
606             //reset flag in case hit this code more than once for some reason (just safety)
607             testGeneratedInterrupt = false;
608 
609             if (theTestPassed == false) {
610                 throw new RuntimeException(failureMessage);
611             }
612         }
613 
614     }//main
615 
616     /**
617      * Test will fail if not passed after this timeout. Default timeout is 300 seconds.
618      * @param seconds timeout in seconds
619      */
setTimeoutTo(int seconds)620     public static synchronized void setTimeoutTo(int seconds) {
621         sleepTime = seconds * 1000;
622     }
623 
624     /**
625      * Set test as passed. Usually shoudn't be called directly.
626      */
pass()627     public static synchronized void pass() {
628         System.out.println("The test passed.");
629         System.out.println("The test is over, hit  Ctl-C to stop Java VM");
630         //first check if this is executing in main thread
631         if (mainThread == Thread.currentThread()) {
632             //Still in the main thread, so set the flag just for kicks,
633             // and throw a test passed exception which will be caught
634             // and end the test.
635             theTestPassed = true;
636             throw new TestPassedException();
637         }
638         theTestPassed = true;
639         testGeneratedInterrupt = true;
640         mainThread.interrupt();
641     }//pass()
642 
643     /**
644      * Fail test generic message.
645      */
fail()646     public static synchronized void fail() {
647         //test writer didn't specify why test failed, so give generic
648         fail("it just plain failed! :-)");
649     }
650 
651     /**
652      * Fail test providing specific reason.
653      * @param whyFailed reason
654      */
fail(String whyFailed)655     public static synchronized void fail(String whyFailed) {
656         System.out.println("The test failed: " + whyFailed);
657         System.out.println("The test is over, hit  Ctl-C to stop Java VM");
658         //check if this called from main thread
659         if (mainThread == Thread.currentThread()) {
660             //If main thread, fail now 'cause not sleeping
661             throw new RuntimeException(whyFailed);
662         }
663         theTestPassed = false;
664         testGeneratedInterrupt = true;
665         failureMessage = whyFailed;
666         mainThread.interrupt();
667     }//fail()
668 }// class LWComboBox
669 class TestPassedException extends RuntimeException {
670 }
671