1 /*
   2  * Copyright (c) 2014, 2015, 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         //*** Create instructions for the user here ***
 554         //System.setProperty("sun.awt.disableMixing", "true");
 555 
 556         String[] instructions = {
 557             "This is an AUTOMATIC test, simply wait until it is done.",
 558             "The result (passed or failed) will be shown in the",
 559             "message window below."
 560         };
 561         Sysout.createDialog();
 562         Sysout.printInstructions(instructions);
 563 
 564         instance.testAwtControls();
 565 
 566         if (wasHWClicked) {
 567             fail("HW component received the click.");
 568             passed = false;
 569         }
 570         if (passed) {
 571             pass();
 572         }
 573     }//End  init()
 574     private static boolean theTestPassed = false;
 575     private static boolean testGeneratedInterrupt = false;
 576     private static String failureMessage = "";
 577     private static Thread mainThread = null;
 578     private static int sleepTime = 300000;
 579 
 580     // Not sure about what happens if multiple of this test are
 581     //  instantiated in the same VM.  Being static (and using
 582     //  static vars), it aint gonna work.  Not worrying about
 583     //  it for now.
 584     /**
 585      * Starting point for test runs. See usage at {@link OverlappingTestBase }
 586      * @param args regular main args, not used.
 587      * @throws InterruptedException
 588      */
 589     public static void doMain(String args[]) throws InterruptedException {
 590         mainThread = Thread.currentThread();
 591         try {
 592             init();
 593         } catch (TestPassedException e) {
 594             //The test passed, so just return from main and harness will
 595             // interepret this return as a pass
 596             return;
 597         }
 598         //At this point, neither test pass nor test fail has been
 599         // called -- either would have thrown an exception and ended the
 600         // test, so we know we have multiple threads.
 601 
 602         //Test involves other threads, so sleep and wait for them to
 603         // called pass() or fail()
 604         try {
 605             Thread.sleep(sleepTime);
 606             //Timed out, so fail the test
 607             throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds");
 608         } catch (InterruptedException e) {
 609             //The test harness may have interrupted the test.  If so, rethrow the exception
 610             // so that the harness gets it and deals with it.
 611             if (!testGeneratedInterrupt) {
 612                 throw e;
 613             }
 614 
 615             //reset flag in case hit this code more than once for some reason (just safety)
 616             testGeneratedInterrupt = false;
 617 
 618             if (theTestPassed == false) {
 619                 throw new RuntimeException(failureMessage);
 620             }
 621         }
 622 
 623     }//main
 624 
 625     /**
 626      * Test will fail if not passed after this timeout. Default timeout is 300 seconds.
 627      * @param seconds timeout in seconds
 628      */
 629     public static synchronized void setTimeoutTo(int seconds) {
 630         sleepTime = seconds * 1000;
 631     }
 632 
 633     /**
 634      * Set test as passed. Usually shoudn't be called directly.
 635      */
 636     public static synchronized void pass() {
 637         Sysout.println("The test passed.");
 638         Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
 639         //first check if this is executing in main thread
 640         if (mainThread == Thread.currentThread()) {
 641             //Still in the main thread, so set the flag just for kicks,
 642             // and throw a test passed exception which will be caught
 643             // and end the test.
 644             theTestPassed = true;
 645             throw new TestPassedException();
 646         }
 647         theTestPassed = true;
 648         testGeneratedInterrupt = true;
 649         mainThread.interrupt();
 650     }//pass()
 651 
 652     /**
 653      * Fail test generic message.
 654      */
 655     public static synchronized void fail() {
 656         //test writer didn't specify why test failed, so give generic
 657         fail("it just plain failed! :-)");
 658     }
 659 
 660     /**
 661      * Fail test providing specific reason.
 662      * @param whyFailed reason
 663      */
 664     public static synchronized void fail(String whyFailed) {
 665         Sysout.println("The test failed: " + whyFailed);
 666         Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
 667         //check if this called from main thread
 668         if (mainThread == Thread.currentThread()) {
 669             //If main thread, fail now 'cause not sleeping
 670             throw new RuntimeException(whyFailed);
 671         }
 672         theTestPassed = false;
 673         testGeneratedInterrupt = true;
 674         failureMessage = whyFailed;
 675         mainThread.interrupt();
 676     }//fail()
 677 }// class LWComboBox
 678 class TestPassedException extends RuntimeException {
 679 }
 680 
 681 //*********** End Standard Test Machinery Section **********
 682 //************ Begin classes defined for the test ****************
 683 // if want to make listeners, here is the recommended place for them, then instantiate
 684 //  them in init()
 685 
 686 /* Example of a class which may be written as part of a test
 687 class NewClass implements anInterface
 688 {
 689 static int newVar = 0;
 690 
 691 public void eventDispatched(AWTEvent e)
 692 {
 693 //Counting events to see if we get enough
 694 eventCount++;
 695 
 696 if( eventCount == 20 )
 697 {
 698 //got enough events, so pass
 699 
 700 LWComboBox.pass();
 701 }
 702 else if( tries == 20 )
 703 {
 704 //tried too many times without getting enough events so fail
 705 
 706 LWComboBox.fail();
 707 }
 708 
 709 }// eventDispatched()
 710 
 711 }// NewClass class
 712 
 713  */
 714 //************** End classes defined for the test *******************
 715 /****************************************************
 716 Standard Test Machinery
 717 DO NOT modify anything below -- it's a standard
 718 chunk of code whose purpose is to make user
 719 interaction uniform, and thereby make it simpler
 720 to read and understand someone else's test.
 721  ****************************************************/
 722 /**
 723 This is part of the standard test machinery.
 724 It creates a dialog (with the instructions), and is the interface
 725 for sending text messages to the user.
 726 To print the instructions, send an array of strings to Sysout.createDialog
 727 WithInstructions method.  Put one line of instructions per array entry.
 728 To display a message for the tester to see, simply call Sysout.println
 729 with the string to be displayed.
 730 This mimics System.out.println but works within the test harness as well
 731 as standalone.
 732  */
 733 class Sysout {
 734     private static TestDialog dialog;
 735 
 736     public static void createDialogWithInstructions(String[] instructions) {
 737         dialog = new TestDialog(new Frame(), "Instructions");
 738         dialog.printInstructions(instructions);
 739         //dialog.setVisible(true);
 740         println("Any messages for the tester will display here.");
 741     }
 742 
 743     public static void createDialog() {
 744         dialog = new TestDialog(new Frame(), "Instructions");
 745         String[] defInstr = {"Instructions will appear here. ", ""};
 746         dialog.printInstructions(defInstr);
 747         //dialog.setVisible(true);
 748         println("Any messages for the tester will display here.");
 749     }
 750 
 751     public static void printInstructions(String[] instructions) {
 752         dialog.printInstructions(instructions);
 753     }
 754 
 755     public static void println(String messageIn) {
 756         dialog.displayMessage(messageIn);
 757         System.out.println(messageIn);
 758     }
 759 }// Sysout  class
 760 
 761 /**
 762 This is part of the standard test machinery.  It provides a place for the
 763 test instructions to be displayed, and a place for interactive messages
 764 to the user to be displayed.
 765 To have the test instructions displayed, see Sysout.
 766 To have a message to the user be displayed, see Sysout.
 767 Do not call anything in this dialog directly.
 768  */
 769 class TestDialog extends Dialog {
 770     TextArea instructionsText;
 771     TextArea messageText;
 772     int maxStringLength = 80;
 773 
 774     //DO NOT call this directly, go through Sysout
 775     public TestDialog(Frame frame, String name) {
 776         super(frame, name);
 777         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 778         instructionsText = new TextArea("", 15, maxStringLength, scrollBoth);
 779         add("North", instructionsText);
 780 
 781         messageText = new TextArea("", 5, maxStringLength, scrollBoth);
 782         add("Center", messageText);
 783 
 784         pack();
 785 
 786        //setVisible(true);
 787     }// TestDialog()
 788 
 789     //DO NOT call this directly, go through Sysout
 790     public void printInstructions(String[] instructions) {
 791         //Clear out any current instructions
 792         instructionsText.setText("");
 793 
 794         //Go down array of instruction strings
 795 
 796         String printStr, remainingStr;
 797         for (int i = 0; i < instructions.length; i++) {
 798             //chop up each into pieces maxSringLength long
 799             remainingStr = instructions[i];
 800             while (remainingStr.length() > 0) {
 801                 //if longer than max then chop off first max chars to print
 802                 if (remainingStr.length() >= maxStringLength) {
 803                     //Try to chop on a word boundary
 804                     int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1);
 805 
 806                     if (posOfSpace <= 0) {
 807                         posOfSpace = maxStringLength - 1;
 808                     }
 809 
 810                     printStr = remainingStr.substring(0, posOfSpace + 1);
 811                     remainingStr = remainingStr.substring(posOfSpace + 1);
 812                 } //else just print
 813                 else {
 814                     printStr = remainingStr;
 815                     remainingStr = "";
 816                 }
 817 
 818                 instructionsText.append(printStr + "\n");
 819 
 820             }// while
 821 
 822         }// for
 823 
 824     }//printInstructions()
 825 
 826     //DO NOT call this directly, go through Sysout
 827     public void displayMessage(String messageIn) {
 828         messageText.append(messageIn + "\n");
 829         System.out.println(messageIn);
 830     }
 831 }// TestDialog  class
 832