1 /*
   2  * Copyright (c) 2017, 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  * @test
  25  * @bug 8180370
  26  * @summary Checks whether Special Chars are skipped while inputting Korean Text
  27  * @requires (os.family == "mac")
  28  * @run main/manual MissingSpecialCharsTest
  29  */
  30 
  31 /**
  32  * This test requires a manual intervention as the keyboard layout has to be 
  33  * changed to 2-set Korean. Once the keyboard layout has been selected, click on
  34  * Start Test to start the automated tests.
  35  */
  36 
  37 import java.awt.AWTException;
  38 import java.awt.Font;
  39 import java.awt.BorderLayout;
  40 import java.awt.Dimension;
  41 import java.awt.FlowLayout;
  42 import java.awt.Robot;
  43 import java.awt.event.WindowAdapter;
  44 import java.awt.event.WindowEvent;
  45 import javax.swing.JButton;
  46 import javax.swing.JFrame;
  47 import javax.swing.JPanel;
  48 import javax.swing.JTextArea;
  49 import javax.swing.SwingUtilities;
  50 import javax.swing.WindowConstants;
  51 import java.awt.event.KeyEvent;
  52 import java.util.Timer;
  53 import java.util.TimerTask;
  54 import javax.swing.JLabel;
  55 import javax.swing.JTextField;
  56 
  57 public class MissingSpecialCharsTest {
  58     private static Thread mainThread = null;
  59     private static boolean testPassed = false;
  60     private static boolean testGeneratedInterrupt = false;
  61     private static JFrame frame = null;
  62     private static int expectedResults[] = null;
  63     private static int inKeyCodes[][] = null;
  64     private static JLabel lblTestStatus = null;
  65     private static String testResult;
  66 
  67     public static void main(String[] args) throws Exception {
  68         mainThread = Thread.currentThread();
  69         SwingUtilities.invokeAndWait(() -> {
  70             setupUI();
  71             });
  72         try {
  73             Thread.sleep(300000);
  74         } catch (InterruptedException e) {
  75             if (!testPassed && testGeneratedInterrupt) {
  76                 throw new RuntimeException("Korean text missing characters : "
  77                                             + testResult);
  78             }
  79         }
  80 
  81         if (testPassed) {
  82             System.out.println(testResult);
  83         } else if (!testGeneratedInterrupt) {
  84             throw new RuntimeException("User has not executed the test");
  85         }
  86     }
  87 
  88     private static void setupUI() {
  89         String description = " Select (2-Set) Korean Keyboard Layout \n"
  90                                 + " And then click on Start Test \n\n"
  91                                 + " Note : To check whether the keyboard layout"
  92                                 + " has been changed properly\n"
  93                                 + " you can try typing in the text field before"
  94                                 + " clicking on start test\n";
  95         String title = "Marked Text Manual Test";
  96 
  97         frame = new JFrame(title);
  98 
  99         JPanel mainPanel = new JPanel(new BorderLayout());
 100 
 101         JPanel textEditPanel = new JPanel(new FlowLayout());
 102 
 103         JTextField textFieldMain = new JTextField(20);
 104         Font font = new Font("Source Han Serif K", Font.BOLD,12);
 105         textFieldMain.setFont(font);
 106 
 107         textEditPanel.add(textFieldMain);
 108 
 109         mainPanel.add(textEditPanel, BorderLayout.CENTER);
 110 
 111         JTextArea textArea = new JTextArea(description);
 112         textArea.setEditable(false);
 113         final JButton btnStartTest = new JButton("Start Test");
 114         final JButton btnCancelTest = new JButton("Cancel Test");
 115 
 116         btnStartTest.addActionListener((e) -> {
 117             btnStartTest.setEnabled(false);
 118             btnCancelTest.setEnabled(false);
 119             glyphTest(textFieldMain);
 120         });
 121 
 122         btnCancelTest.addActionListener((e) -> {
 123             frame.dispose();
 124             testGeneratedInterrupt = false;
 125             mainThread.interrupt();
 126         });
 127         mainPanel.add(textArea, BorderLayout.NORTH);
 128 
 129         JPanel buttonPanel = new JPanel(new FlowLayout());
 130         buttonPanel.add(btnStartTest);
 131         buttonPanel.add(btnCancelTest);
 132         mainPanel.add(buttonPanel, BorderLayout.SOUTH);
 133 
 134         lblTestStatus = new JLabel("");
 135         lblTestStatus.setMinimumSize(new Dimension(150, 20));
 136         lblTestStatus.setPreferredSize(new Dimension(150, 20));
 137         lblTestStatus.setVisible(true);
 138         textEditPanel.add(lblTestStatus);
 139 
 140         frame.add(mainPanel);
 141         frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 142         frame.pack();
 143         frame.setLocationRelativeTo(null);
 144 
 145         frame.addWindowListener(new WindowAdapter() {
 146             @Override
 147             public void windowClosing(WindowEvent e) {
 148                 testGeneratedInterrupt = false;
 149                 mainThread.interrupt();
 150             }
 151             @Override
 152             public void windowOpened( WindowEvent e ){
 153                 textFieldMain.requestFocusInWindow();
 154             }
 155         });
 156 
 157         frame.setVisible(true);
 158     }
 159 
 160     private static void glyphTest(JTextField textFieldToUse) {
 161         try {
 162             Robot robotKeySimulator = new Robot();
 163 
 164             scheduleKeyInputProcess(textFieldToUse, robotKeySimulator);
 165         }
 166         catch(AWTException e) {
 167             System.err.print("Creation Of Robot Failed : " + e.getMessage());
 168             testResults(false);
 169         }
 170     }
 171 
 172     private static void scheduleKeyInputProcess(JTextField textFieldToUse,
 173                                                 Robot robotKeyInput) {
 174         textFieldToUse.setText("");
 175         lblTestStatus.setText("Running Tests..");
 176         textFieldToUse.requestFocusInWindow();
 177 
 178         Timer timer = new Timer(false);
 179         long intervalWait = 2 * 1000; //2 Seconds
 180         timer.schedule(new keyInputScheduler(timer, 
 181                             textFieldToUse, robotKeyInput), 
 182                             intervalWait, intervalWait);
 183     }
 184 
 185     private static boolean setKeyInput(int iCount) {
 186         boolean inputSet = true;
 187 
 188         final int iExclamation = KeyEvent.getExtendedKeyCodeForChar('!');
 189 
 190         if (iExclamation != KeyEvent.VK_UNDEFINED) {
 191             switch(iCount) {
 192                 case 0:
 193                     // Input Korean q (#12610) /(#47)
 194                     expectedResults = new int[]{ 12610, 47 }; 
 195                     inKeyCodes = new int[][] {  {KeyEvent.VK_Q},
 196                                                 {KeyEvent.VK_SLASH}
 197                                             };
 198                     break;
 199 
 200                 case 1:
 201                     // Input Korean q (#12610) /(#47) gh (#54840) \(#92)
 202                     expectedResults = new int[]{ 12610, 47, 54840, 92 }; 
 203                     inKeyCodes = new int[][] {  {KeyEvent.VK_Q},
 204                                                 {KeyEvent.VK_SLASH},
 205                                                 {KeyEvent.VK_G},
 206                                                 {KeyEvent.VK_H},
 207                                                 {KeyEvent.VK_BACK_SLASH}
 208                                             };
 209                     break;
 210 
 211                 case 2:
 212                     // Input Korean q (#12610) /(#47) ghq (#54857) \(#92)
 213                     expectedResults = new int[]{ 12610, 47, 54857, 92 }; 
 214                     inKeyCodes = new int[][] {  {KeyEvent.VK_Q},
 215                                                 {KeyEvent.VK_SLASH},
 216                                                 {KeyEvent.VK_G},
 217                                                 {KeyEvent.VK_H},
 218                                                 {KeyEvent.VK_Q},
 219                                                 {KeyEvent.VK_BACK_SLASH}
 220                                             };
 221                     break;
 222 
 223                 case 3:
 224                     // Input Korean q (#12610) /(#47) gh (#54840) \(#92)
 225                     expectedResults = new int[]{ 12610, 47, 54840, 92 }; 
 226                     inKeyCodes = new int[][] {  {KeyEvent.VK_Q},
 227                                                 {KeyEvent.VK_SLASH},
 228                                                 {KeyEvent.VK_G},
 229                                                 {KeyEvent.VK_H},
 230                                                 {KeyEvent.VK_Q},
 231                                                 {KeyEvent.VK_BACK_SPACE},
 232                                                 {KeyEvent.VK_BACK_SLASH}
 233                                             };
 234                     break;
 235 
 236                 case 4:
 237                     // Input Korean q (#12610) /(#47) g (#12622) \(#92)
 238                     expectedResults = new int[]{ 12610, 47, 12622, 92 }; 
 239                     inKeyCodes = new int[][] {  {KeyEvent.VK_Q},
 240                                                 {KeyEvent.VK_SLASH},
 241                                                 {KeyEvent.VK_G},
 242                                                 {KeyEvent.VK_H},
 243                                                 {KeyEvent.VK_Q},
 244                                                 {KeyEvent.VK_BACK_SPACE},
 245                                                 {KeyEvent.VK_BACK_SPACE},
 246                                                 {KeyEvent.VK_BACK_SLASH}
 247                                             };
 248                     break;
 249 
 250                 default:
 251                     inputSet = false;
 252                     break;
 253             }
 254         }
 255 
 256         return inputSet;
 257     }
 258     
 259     private static void enterInput(Robot robotKeyInput,
 260                                     int keyInputs[]) {
 261         String strKeyInput = "KeyPress=>";
 262         final int noOfKeyInputs = keyInputs.length;
 263         for (int i = 0; i < noOfKeyInputs; i++) {
 264             robotKeyInput.keyPress(keyInputs[i]);
 265             strKeyInput += (Integer.toHexString(keyInputs[i])) + ":" ;
 266         }
 267 
 268         strKeyInput += "KeyRelease=>";
 269         for (int i = noOfKeyInputs - 1; i >= 0; i--) {
 270             robotKeyInput.keyRelease(keyInputs[i]);
 271             strKeyInput += (Integer.toHexString(keyInputs[i])) + ":" ;
 272         }
 273         System.out.println(strKeyInput);
 274     }
 275 
 276     public static synchronized void testResults(boolean isPassed) {
 277         frame.dispose();
 278         testPassed = isPassed;
 279         testGeneratedInterrupt = true;
 280         mainThread.interrupt();
 281     }
 282 
 283     private static boolean validateInput(JTextField textFieldToUse) {
 284         boolean validateRes = false;
 285 
 286         if (expectedResults != null) {
 287             String strCurr = textFieldToUse.getText();
 288             if (expectedResults.length == strCurr.length()) {
 289                 validateRes = true;
 290 
 291                 for (int i = 0; i < strCurr.length(); i++) {
 292                     final int charActual = strCurr.charAt(i);
 293                     if (charActual != expectedResults[i]) {
 294                         System.err.println("<" + i + "> Actual = " + charActual 
 295                                         + " Expected = " + expectedResults[i]);
 296                         validateRes = false;
 297                         break;
 298                     }
 299                 }
 300             }
 301         }
 302 
 303         return validateRes;
 304     }
 305 
 306     static class keyInputScheduler extends TimerTask {
 307         private final Timer timer;
 308         private final JTextField textFieldToValidate;
 309         private final Robot robotForKeyInput;
 310 
 311         private int keyCount = 0;
 312         private int taskCount = 0;
 313         private boolean taskSet = false;
 314         private boolean allTasksPerformed = false;
 315         private boolean validationRes = false;
 316 
 317         public keyInputScheduler(Timer timertoUse, 
 318                                     JTextField textFieldToUse,
 319                                     Robot robotKeyInput) {
 320             timer = timertoUse;
 321             textFieldToValidate = textFieldToUse;
 322             robotForKeyInput = robotKeyInput;
 323         }
 324 
 325         @Override
 326         public void run() {
 327             performTasks();
 328         }
 329 
 330         public synchronized void performTasks() {
 331             if (allTasksPerformed) {
 332                 stopTask();
 333             }
 334             if (!taskSet) {
 335                 if (!setKeyInput(taskCount)) {
 336                     allTasksPerformed = true;
 337                     setTaskStatus();
 338                 } else {
 339                     taskCount++;
 340                     taskSet = true;
 341                     keyCount = 0;
 342                 }
 343             }
 344 
 345             if (taskSet) {
 346                 final int iNoOfKeyInputs = inKeyCodes.length;
 347                 if (keyCount < iNoOfKeyInputs) {
 348                     enterInput(robotForKeyInput, inKeyCodes[keyCount]);
 349                     keyCount++;
 350                 } else {
 351                     validationRes = validateInput(textFieldToValidate);
 352                     if (!validationRes) {
 353                         allTasksPerformed = true;
 354                     } else {
 355                         taskSet = false;
 356                         textFieldToValidate.setText("");
 357                     }
 358                     setTaskStatus();
 359                 }
 360             }
 361         }
 362 
 363         public void stopTask() {
 364             timer.cancel();
 365             testResults(validationRes);
 366         }
 367 
 368         public void setTaskStatus() {
 369             if (validationRes) {
 370                 if (allTasksPerformed) {
 371                     testResult = "All Tests Passed";
 372                 } else {
 373                     testResult = "Test " +  Integer.toString(taskCount) 
 374                                     + " Passed";
 375                 }
 376             } else {
 377                 testResult = "Test " + Integer.toString(taskCount) 
 378                                 + " Failed";
 379             }
 380             lblTestStatus.setText(testResult); 
 381         }
 382     }
 383 }