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.Field;
  29 import java.lang.reflect.InvocationTargetException;
  30 import java.lang.reflect.Method;
  31 import java.util.ArrayList;
  32 import javax.swing.*;
  33 
  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     static Field peerField;
  78     // "properties"
  79     /**
  80      * Tests customization. Set this variable to test only control from java.awt
  81      * <p>Usage of this variable should be marked with CR being the reason.
  82      * <p>Do not use this variable simultaneously with {@link OverlappingTestBase#skipClassNames}
  83      */
  84     protected String onlyClassName = null;
  85     /**
  86      * For customizing tests. List classes' simple names to skip them from testings.
  87      * <p>Usage of this variable should be marked with CR being the reason.
  88      */
  89     protected String[] skipClassNames = null;
  90     /**
  91      * Set to false to avoid event delivery validation
  92      * @see OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean)
  93      */
  94     protected boolean useClickValidation = true;
  95     /**
  96      * Set to false if test doesn't supposed to verify EmbeddedFrame
  97      */
  98     protected boolean testEmbeddedFrame = false;
  99     /**
 100      * Set this variable to true if testing embedded frame is impossible (on Mac, for instance, for now).
 101      * The testEmbeddedFrame is explicitly set to true in dozen places.
 102      */
 103     protected boolean skipTestingEmbeddedFrame = false;
 104 
 105     public static final boolean isMac = System.getProperty("os.name").toLowerCase().contains("os x");
 106     private boolean isFrameBorderCalculated;
 107     private int borderShift;
 108 
 109     {    if (Toolkit.getDefaultToolkit().getClass().getName().matches(".*L.*Toolkit")) {
 110              // No EmbeddedFrame in LWToolkit/LWCToolkit, yet
 111              // And it should be programmed some other way, too, in any case
 112              System.err.println("skipTestingEmbeddedFrame");
 113              skipTestingEmbeddedFrame = true;
 114          }else {
 115              System.err.println("do not skipTestingEmbeddedFrame");
 116          }
 117     }
 118 
 119     protected int frameBorderCounter() {
 120         if (!isFrameBorderCalculated) {
 121             try {
 122                 new FrameBorderCounter(); // force compilation by jtreg
 123                 String JAVA_HOME = System.getProperty("java.home");
 124                 Process p = Runtime.getRuntime().exec(JAVA_HOME + "/bin/java FrameBorderCounter");
 125                 try {
 126                     p.waitFor();
 127                 } catch (InterruptedException e) {
 128                     e.printStackTrace();
 129                     throw new RuntimeException(e);
 130                 }
 131                 if (p.exitValue() != 0) {
 132                     throw new RuntimeException("FrameBorderCounter exited with not null code!\n" + readInputStream(p.getErrorStream()));
 133                 }
 134                 borderShift = Integer.parseInt(readInputStream(p.getInputStream()).trim());
 135                 isFrameBorderCalculated = true;
 136             } catch (IOException e) {
 137                 e.printStackTrace();
 138                 throw new RuntimeException("Problem calculating a native border size");
 139             }
 140         }
 141         return borderShift;
 142     }
 143 
 144     public void getVerifyColor() {
 145         try {
 146             final int size = 200;
 147             final Point[] p = new Point[1];
 148             SwingUtilities.invokeAndWait(new Runnable() {
 149                 public void run(){
 150                     JFrame frame = new JFrame("set back");
 151                     frame.getContentPane().setBackground(AWT_BACKGROUND_COLOR);
 152                     frame.setSize(size, size);
 153                     frame.setUndecorated(true);
 154                     frame.setVisible(true);
 155                     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 156                     p[0] = frame.getLocation();
 157                 }
 158             });
 159             Robot robot = new Robot();
 160             robot.waitForIdle();
 161             Thread.sleep(ROBOT_DELAY);
 162             AWT_VERIFY_COLOR = robot.getPixelColor(p[0].x+size/2, p[0].y+size/2);
 163             System.out.println("Color will be compared with " + AWT_VERIFY_COLOR + " instead of " + AWT_BACKGROUND_COLOR);
 164         } catch (Exception e) {
 165             System.err.println("Cannot get verify color: "+e.getMessage());
 166         }
 167     }
 168 
 169     private String readInputStream(InputStream is) throws IOException {
 170         byte[] buffer = new byte[4096];
 171         int len = 0;
 172         StringBuilder sb = new StringBuilder();
 173         try (InputStreamReader isr = new InputStreamReader(is)) {
 174             while ((len = is.read(buffer)) > 0) {
 175                 sb.append(new String(buffer, 0, len));
 176             }
 177         }
 178         return sb.toString();
 179     }
 180 
 181     private void setupControl(final Component control) {
 182         if (useClickValidation) {
 183             control.addMouseListener(new MouseAdapter() {
 184                 @Override
 185                 public void mouseClicked(MouseEvent e) {
 186                     System.err.println("ERROR: " + control.getClass() + " received mouse click.");
 187                     wasHWClicked = true;
 188                 }
 189             });
 190         }
 191         control.setBackground(AWT_BACKGROUND_COLOR);
 192         control.setForeground(AWT_BACKGROUND_COLOR);
 193         control.setPreferredSize(new Dimension(150, 150));
 194         control.setFocusable(false);
 195     }
 196 
 197     private void addAwtControl(java.util.List<Component> container, final Component control) {
 198         String simpleName = control.getClass().getSimpleName();
 199         if (onlyClassName != null && !simpleName.equals(onlyClassName)) {
 200             return;
 201         }
 202         if (skipClassNames != null) {
 203             for (String skipMe : skipClassNames) {
 204                 if (simpleName.equals(skipMe)) {
 205                     return;
 206                 }
 207             }
 208         }
 209         setupControl(control);
 210         container.add(control);
 211     }
 212 
 213     private void addSimpleAwtControl(java.util.List<Component> container, String className) {
 214         try {
 215             Class definition = Class.forName("java.awt." + className);
 216             Constructor constructor = definition.getConstructor(new Class[]{String.class});
 217             java.awt.Component component = (java.awt.Component) constructor.newInstance(new Object[]{"AWT Component " + className});
 218             addAwtControl(container, component);
 219         } catch (Exception ex) {
 220             System.err.println(ex.getMessage());
 221             fail("Setup error, this jdk doesn't have awt conrol " + className);
 222         }
 223     }
 224 
 225     /**
 226      * Adds current AWT control to container
 227      * <p>N.B.: if testEmbeddedFrame == true this method will also add EmbeddedFrame over Canvas
 228      * and it should be called <b>after</b> Frame.setVisible(true) call
 229      * @param container container to hold AWT component
 230      */
 231     protected final void propagateAWTControls(Container container) {
 232         if (currentAwtControl != null) {
 233             container.add(currentAwtControl);
 234         } else { // embedded frame
 235             try {
 236 
 237                 //create embedder
 238                 Canvas embedder = new Canvas();
 239                 embedder.setBackground(Color.RED);
 240                 embedder.setPreferredSize(new Dimension(150, 150));
 241                 container.add(embedder);
 242                 container.setVisible(true); // create peer
 243 
 244                 long frameWindow = 0;
 245                 String getWindowMethodName = "getHWnd";
 246                 if (Toolkit.getDefaultToolkit().getClass().getName().contains("XToolkit")) {
 247                     getWindowMethodName = "getWindow";
 248                 }
 249                 ComponentPeer peer = (ComponentPeer) peerField.get(embedder);
 250                 //  System.err.println("Peer: " + peer);
 251                 Method getWindowMethod = peer.getClass().getMethod(getWindowMethodName);
 252                 frameWindow = (Long) getWindowMethod.invoke(peer);
 253 //                System.err.println("frame peer ID: " + frameWindow);
 254 
 255                 String eframeClassName = "sun.awt.windows.WEmbeddedFrame";
 256                 if (Toolkit.getDefaultToolkit().getClass().getName().contains("XToolkit")) {
 257                     eframeClassName = "sun.awt.X11.XEmbeddedFrame";
 258                 }
 259                 Class eframeClass = Class.forName(eframeClassName);
 260                 Constructor eframeCtor = eframeClass.getConstructor(long.class);
 261                 EmbeddedFrame eframe = (EmbeddedFrame) eframeCtor.newInstance(frameWindow);
 262                 setupControl(eframe);
 263                 eframe.setSize(new Dimension(150, 150));
 264                 eframe.setVisible(true);
 265 //                System.err.println(eframe.getSize());
 266             } catch (Exception ex) {
 267                 ex.printStackTrace();
 268                 fail("Failed to instantiate EmbeddedFrame: " + ex.getMessage());
 269             }
 270         }
 271     }
 272     private static final Font hugeFont = new Font("Arial", Font.BOLD, 70);
 273 
 274     private java.util.List<Component> getAWTControls() {
 275         java.util.List<Component> components = new ArrayList<Component>();
 276 
 277         for (String clazz : simpleAwtControls) {
 278             addSimpleAwtControl(components, clazz);
 279         }
 280 
 281         TextField tf = new TextField();
 282         tf.setFont(hugeFont);
 283         addAwtControl(components, tf);
 284 
 285         // more complex controls
 286         Choice c = new Choice();
 287         for (int i = 0; i < petStrings.length; i++) {
 288             c.add(petStrings[i]);
 289         }
 290         addAwtControl(components, c);
 291         c.setPreferredSize(null);
 292         c.setFont(hugeFont); // to make control bigger as setPrefferedSize don't do his job here
 293 
 294         List l = new List(petStrings.length);
 295         for (int i = 0; i < petStrings.length; i++) {
 296             l.add(petStrings[i]);
 297         }
 298         addAwtControl(components, l);
 299 
 300         Canvas canvas = new Canvas();
 301         canvas.setSize(100, 200);
 302         addAwtControl(components, canvas);
 303 
 304         Scrollbar sb = new Scrollbar(Scrollbar.VERTICAL, 500, 1, 0, 500);
 305         addAwtControl(components, sb);
 306 
 307         Scrollbar sb2 = new Scrollbar(Scrollbar.HORIZONTAL, 500, 1, 0, 500);
 308         addAwtControl(components, sb2);
 309 
 310         return components;
 311     }
 312     /**
 313      * Default shift for {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point) }
 314      */
 315     protected static Point shift = new Point(16, 16);
 316 
 317     /**
 318      * Verifies point using specified AWT Robot. Supposes <code>defaultShift == true</code> for {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean) }.
 319      * This method is used to verify controls by providing just their plain screen coordinates.
 320      * @param robot AWT Robot. Usually created by {@link Util#createRobot() }
 321      * @param lLoc point to verify
 322      * @see OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean)
 323      */
 324     protected void clickAndBlink(Robot robot, Point lLoc) {
 325         clickAndBlink(robot, lLoc, true);
 326     }
 327     /**
 328      * Default failure message for color check
 329      * @see OverlappingTestBase#performTest()
 330      */
 331     protected String failMessageColorCheck = "The LW component did not pass pixel color check and is overlapped";
 332     /**
 333      * Default failure message event check
 334      * @see OverlappingTestBase#performTest()
 335      */
 336     protected String failMessage = "The LW component did not received the click.";
 337 
 338     private static boolean isValidForPixelCheck(Component component) {
 339         if ((component instanceof java.awt.Scrollbar) || isMac && (component instanceof java.awt.Button)) {
 340             return false;
 341         }
 342         return true;
 343     }
 344 
 345     /**
 346      * Preliminary validation - should be run <b>before</b> overlapping happens to ensure test is correct.
 347      * @param robot AWT Robot. Usually created by {@link Util#createRobot() }
 348      * @param lLoc point to validate to be <b>of</b> {@link OverlappingTestBase#AWT_BACKGROUND_COLOR}
 349      * @param component tested component, should be pointed out as not all components are valid for pixel check.
 350      */
 351     protected void pixelPreCheck(Robot robot, Point lLoc, Component component) {
 352         if (isValidForPixelCheck(component)) {
 353             int tries = 10;
 354             Color c = null;
 355             while (tries-- > 0) {
 356                 c = robot.getPixelColor(lLoc.x, lLoc.y);
 357                 System.out.println("Precheck. color: "+c+" compare with "+AWT_VERIFY_COLOR);
 358                 if (c.equals(AWT_VERIFY_COLOR)) {
 359                     return;
 360                 }
 361                 try {
 362                     Thread.sleep(100);
 363                 } catch (InterruptedException e) {
 364                 }
 365             }
 366             System.err.println(lLoc + ": " + c);
 367             fail("Dropdown test setup failure, colored part of AWT component is not located at click area");
 368         }
 369     }
 370 
 371     /**
 372      * Verifies point using specified AWT Robot.
 373      * <p>Firstly, verifies point by color pixel check
 374      * <p>Secondly, verifies event delivery by mouse click
 375      * @param robot AWT Robot. Usually created by {@link Util#createRobot() }
 376      * @param lLoc point to verify
 377      * @param defaultShift if true verified position will be shifted by {@link OverlappingTestBase#shift }.
 378      */
 379     protected void clickAndBlink(Robot robot, Point lLoc, boolean defaultShift) {
 380         Point loc = lLoc.getLocation();
 381         //check color
 382         Util.waitForIdle(robot);
 383         try{
 384             Thread.sleep(500);
 385         }catch(Exception exx){
 386             exx.printStackTrace();
 387         }
 388 
 389         if (defaultShift) {
 390             loc.translate(shift.x, shift.y);
 391         }
 392         if (!(System.getProperty("os.name").toLowerCase().contains("os x"))) {
 393             Color c = robot.getPixelColor(loc.x, loc.y);
 394             System.out.println("C&B. color: "+c+" compare with "+AWT_VERIFY_COLOR);
 395             if (c.equals(AWT_VERIFY_COLOR)) {
 396                 fail(failMessageColorCheck);
 397                 passed = false;
 398             }
 399 
 400             // perform click
 401             Util.waitForIdle(robot);
 402         }
 403 
 404         robot.mouseMove(loc.x, loc.y);
 405 
 406         robot.mousePress(InputEvent.BUTTON1_MASK);
 407         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 408         Util.waitForIdle(robot);
 409     }
 410 
 411     /**
 412      * This method should be overriden with code which setups UI for testing.
 413      * Code in this method <b>will</b> be called only from AWT thread so Swing operations can be called directly.
 414      *
 415      * @see {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) } for instructions about adding tested AWT control to UI
 416      */
 417     protected abstract void prepareControls();
 418 
 419     /**
 420      * 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.
 421      * @return true if test passed. Otherwise fail with default fail message.
 422      * @see {@link OverlappingTestBase#failMessage} default fail message
 423      */
 424     protected abstract boolean performTest();
 425     /**
 426      * This method can be overriden with cleanup routines. It will be called from AWT thread so all Swing operations should be treated accordingly.
 427      */
 428     protected void cleanup() {
 429         // intentionally do nothing
 430     }
 431     /**
 432      * Currect tested AWT Control. Usually shouldn't be accessed directly.
 433      *
 434      * @see {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) } for instructions about adding tested AWT control to UI
 435      */
 436     protected Component currentAwtControl;
 437 
 438     private void testComponent(Component component) throws InterruptedException, InvocationTargetException {
 439         Robot robot = null;
 440         try {
 441             robot = new Robot();
 442         }catch(Exception ignorex) {
 443         }
 444         currentAwtControl = component;
 445         System.out.println("Testing " + currentAwtControl.getClass().getSimpleName());
 446         SwingUtilities.invokeAndWait(new Runnable() {
 447             public void run() {
 448                 prepareControls();
 449             }
 450         });
 451         if (component != null) {
 452             Util.waitTillShown(component);
 453         }
 454         Util.waitForIdle(robot);
 455         try {
 456             Thread.sleep(500); // wait for graphic effects on systems like Win7
 457         } catch (InterruptedException ex) {
 458         }
 459         if (!instance.performTest()) {
 460             fail(failMessage);
 461             passed = false;
 462         }
 463         SwingUtilities.invokeAndWait(new Runnable() {
 464             public void run() {
 465                 cleanup();
 466             }
 467         });
 468     }
 469 
 470     private void testEmbeddedFrame() throws InvocationTargetException, InterruptedException {
 471         Robot robot = null;
 472         try {
 473             robot = new Robot();
 474         }catch(Exception ignorex) {
 475         }
 476         System.out.println("Testing EmbeddedFrame");
 477         currentAwtControl = null;
 478         SwingUtilities.invokeAndWait(new Runnable() {
 479             public void run() {
 480                 prepareControls();
 481             }
 482         });
 483         Util.waitForIdle(robot);
 484         try {
 485             Thread.sleep(500); // wait for graphic effects on systems like Win7
 486         } catch (InterruptedException ex) {
 487         }
 488         if (!instance.performTest()) {
 489             fail(failMessage);
 490             passed = false;
 491         }
 492         SwingUtilities.invokeAndWait(new Runnable() {
 493             public void run() {
 494                 cleanup();
 495             }
 496         });
 497     }
 498 
 499     private void testAwtControls() throws InterruptedException {
 500         try {
 501             for (Component component : getAWTControls()) {
 502                 testComponent(component);
 503             }
 504             if (testEmbeddedFrame && !skipTestingEmbeddedFrame) {
 505                 testEmbeddedFrame();
 506             }
 507         } catch (InvocationTargetException ex) {
 508             ex.printStackTrace();
 509             fail(ex.getMessage());
 510         }
 511     }
 512     /**
 513      * Used by standard test machinery. See usage at {@link OverlappingTestBase }
 514      */
 515     protected static OverlappingTestBase instance;
 516 
 517     protected OverlappingTestBase() {
 518         getVerifyColor();
 519     }
 520 
 521     /*****************************************************
 522      * Standard Test Machinery Section
 523      * DO NOT modify anything in this section -- it's a
 524      * standard chunk of code which has all of the
 525      * synchronisation necessary for the test harness.
 526      * By keeping it the same in all tests, it is easier
 527      * to read and understand someone else's test, as
 528      * well as insuring that all tests behave correctly
 529      * with the test harness.
 530      * There is a section following this for test-
 531      * classes
 532      ******************************************************/
 533     private static void init() throws InterruptedException {
 534         //*** Create instructions for the user here ***
 535         //System.setProperty("sun.awt.disableMixing", "true");
 536 
 537         String[] instructions = {
 538             "This is an AUTOMATIC test, simply wait until it is done.",
 539             "The result (passed or failed) will be shown in the",
 540             "message window below."
 541         };
 542         Sysout.createDialog();
 543         Sysout.printInstructions(instructions);
 544 
 545         instance.testAwtControls();
 546 
 547         if (wasHWClicked) {
 548             fail("HW component received the click.");
 549             passed = false;
 550         }
 551         if (passed) {
 552             pass();
 553         }
 554     }//End  init()
 555     private static boolean theTestPassed = false;
 556     private static boolean testGeneratedInterrupt = false;
 557     private static String failureMessage = "";
 558     private static Thread mainThread = null;
 559     private static int sleepTime = 300000;
 560 
 561     // Not sure about what happens if multiple of this test are
 562     //  instantiated in the same VM.  Being static (and using
 563     //  static vars), it aint gonna work.  Not worrying about
 564     //  it for now.
 565     /**
 566      * Starting point for test runs. See usage at {@link OverlappingTestBase }
 567      * @param args regular main args, not used.
 568      * @throws InterruptedException
 569      */
 570     public static void doMain(String args[]) throws InterruptedException {
 571         try {
 572             peerField = Component.class.getDeclaredField("peer");
 573         } catch (NoSuchFieldException e) {
 574             throw new RuntimeException("peer field is not available");
 575         }
 576         peerField.setAccessible(true);
 577         mainThread = Thread.currentThread();
 578         try {
 579             init();
 580         } catch (TestPassedException e) {
 581             //The test passed, so just return from main and harness will
 582             // interepret this return as a pass
 583             return;
 584         }
 585         //At this point, neither test pass nor test fail has been
 586         // called -- either would have thrown an exception and ended the
 587         // test, so we know we have multiple threads.
 588 
 589         //Test involves other threads, so sleep and wait for them to
 590         // called pass() or fail()
 591         try {
 592             Thread.sleep(sleepTime);
 593             //Timed out, so fail the test
 594             throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds");
 595         } catch (InterruptedException e) {
 596             //The test harness may have interrupted the test.  If so, rethrow the exception
 597             // so that the harness gets it and deals with it.
 598             if (!testGeneratedInterrupt) {
 599                 throw e;
 600             }
 601 
 602             //reset flag in case hit this code more than once for some reason (just safety)
 603             testGeneratedInterrupt = false;
 604 
 605             if (theTestPassed == false) {
 606                 throw new RuntimeException(failureMessage);
 607             }
 608         }
 609 
 610     }//main
 611 
 612     /**
 613      * Test will fail if not passed after this timeout. Default timeout is 300 seconds.
 614      * @param seconds timeout in seconds
 615      */
 616     public static synchronized void setTimeoutTo(int seconds) {
 617         sleepTime = seconds * 1000;
 618     }
 619 
 620     /**
 621      * Set test as passed. Usually shoudn't be called directly.
 622      */
 623     public static synchronized void pass() {
 624         Sysout.println("The test passed.");
 625         Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
 626         //first check if this is executing in main thread
 627         if (mainThread == Thread.currentThread()) {
 628             //Still in the main thread, so set the flag just for kicks,
 629             // and throw a test passed exception which will be caught
 630             // and end the test.
 631             theTestPassed = true;
 632             throw new TestPassedException();
 633         }
 634         theTestPassed = true;
 635         testGeneratedInterrupt = true;
 636         mainThread.interrupt();
 637     }//pass()
 638 
 639     /**
 640      * Fail test generic message.
 641      */
 642     public static synchronized void fail() {
 643         //test writer didn't specify why test failed, so give generic
 644         fail("it just plain failed! :-)");
 645     }
 646 
 647     /**
 648      * Fail test providing specific reason.
 649      * @param whyFailed reason
 650      */
 651     public static synchronized void fail(String whyFailed) {
 652         Sysout.println("The test failed: " + whyFailed);
 653         Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
 654         //check if this called from main thread
 655         if (mainThread == Thread.currentThread()) {
 656             //If main thread, fail now 'cause not sleeping
 657             throw new RuntimeException(whyFailed);
 658         }
 659         theTestPassed = false;
 660         testGeneratedInterrupt = true;
 661         failureMessage = whyFailed;
 662         mainThread.interrupt();
 663     }//fail()
 664 }// class LWComboBox
 665 class TestPassedException extends RuntimeException {
 666 }
 667 
 668 //*********** End Standard Test Machinery Section **********
 669 //************ Begin classes defined for the test ****************
 670 // if want to make listeners, here is the recommended place for them, then instantiate
 671 //  them in init()
 672 
 673 /* Example of a class which may be written as part of a test
 674 class NewClass implements anInterface
 675 {
 676 static int newVar = 0;
 677 
 678 public void eventDispatched(AWTEvent e)
 679 {
 680 //Counting events to see if we get enough
 681 eventCount++;
 682 
 683 if( eventCount == 20 )
 684 {
 685 //got enough events, so pass
 686 
 687 LWComboBox.pass();
 688 }
 689 else if( tries == 20 )
 690 {
 691 //tried too many times without getting enough events so fail
 692 
 693 LWComboBox.fail();
 694 }
 695 
 696 }// eventDispatched()
 697 
 698 }// NewClass class
 699 
 700  */
 701 //************** End classes defined for the test *******************
 702 /****************************************************
 703 Standard Test Machinery
 704 DO NOT modify anything below -- it's a standard
 705 chunk of code whose purpose is to make user
 706 interaction uniform, and thereby make it simpler
 707 to read and understand someone else's test.
 708  ****************************************************/
 709 /**
 710 This is part of the standard test machinery.
 711 It creates a dialog (with the instructions), and is the interface
 712 for sending text messages to the user.
 713 To print the instructions, send an array of strings to Sysout.createDialog
 714 WithInstructions method.  Put one line of instructions per array entry.
 715 To display a message for the tester to see, simply call Sysout.println
 716 with the string to be displayed.
 717 This mimics System.out.println but works within the test harness as well
 718 as standalone.
 719  */
 720 class Sysout {
 721     private static TestDialog dialog;
 722 
 723     public static void createDialogWithInstructions(String[] instructions) {
 724         dialog = new TestDialog(new Frame(), "Instructions");
 725         dialog.printInstructions(instructions);
 726         //dialog.setVisible(true);
 727         println("Any messages for the tester will display here.");
 728     }
 729 
 730     public static void createDialog() {
 731         dialog = new TestDialog(new Frame(), "Instructions");
 732         String[] defInstr = {"Instructions will appear here. ", ""};
 733         dialog.printInstructions(defInstr);
 734         //dialog.setVisible(true);
 735         println("Any messages for the tester will display here.");
 736     }
 737 
 738     public static void printInstructions(String[] instructions) {
 739         dialog.printInstructions(instructions);
 740     }
 741 
 742     public static void println(String messageIn) {
 743         dialog.displayMessage(messageIn);
 744         System.out.println(messageIn);
 745     }
 746 }// Sysout  class
 747 
 748 /**
 749 This is part of the standard test machinery.  It provides a place for the
 750 test instructions to be displayed, and a place for interactive messages
 751 to the user to be displayed.
 752 To have the test instructions displayed, see Sysout.
 753 To have a message to the user be displayed, see Sysout.
 754 Do not call anything in this dialog directly.
 755  */
 756 class TestDialog extends Dialog {
 757     TextArea instructionsText;
 758     TextArea messageText;
 759     int maxStringLength = 80;
 760 
 761     //DO NOT call this directly, go through Sysout
 762     public TestDialog(Frame frame, String name) {
 763         super(frame, name);
 764         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 765         instructionsText = new TextArea("", 15, maxStringLength, scrollBoth);
 766         add("North", instructionsText);
 767 
 768         messageText = new TextArea("", 5, maxStringLength, scrollBoth);
 769         add("Center", messageText);
 770 
 771         pack();
 772 
 773        //setVisible(true);
 774     }// TestDialog()
 775 
 776     //DO NOT call this directly, go through Sysout
 777     public void printInstructions(String[] instructions) {
 778         //Clear out any current instructions
 779         instructionsText.setText("");
 780 
 781         //Go down array of instruction strings
 782 
 783         String printStr, remainingStr;
 784         for (int i = 0; i < instructions.length; i++) {
 785             //chop up each into pieces maxSringLength long
 786             remainingStr = instructions[i];
 787             while (remainingStr.length() > 0) {
 788                 //if longer than max then chop off first max chars to print
 789                 if (remainingStr.length() >= maxStringLength) {
 790                     //Try to chop on a word boundary
 791                     int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1);
 792 
 793                     if (posOfSpace <= 0) {
 794                         posOfSpace = maxStringLength - 1;
 795                     }
 796 
 797                     printStr = remainingStr.substring(0, posOfSpace + 1);
 798                     remainingStr = remainingStr.substring(posOfSpace + 1);
 799                 } //else just print
 800                 else {
 801                     printStr = remainingStr;
 802                     remainingStr = "";
 803                 }
 804 
 805                 instructionsText.append(printStr + "\n");
 806 
 807             }// while
 808 
 809         }// for
 810 
 811     }//printInstructions()
 812 
 813     //DO NOT call this directly, go through Sysout
 814     public void displayMessage(String messageIn) {
 815         messageText.append(messageIn + "\n");
 816         System.out.println(messageIn);
 817     }
 818 }// TestDialog  class
 819