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 
 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 
 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 
 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 
 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 
 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 
 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      */
 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 
 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      */
 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 
 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      */
 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      */
 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      */
 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      */
 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      */
 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 
 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 
 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 
 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 
 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      ******************************************************/
 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      */
 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      */
 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      */
 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      */
 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      */
 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 }