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