import java.awt.AWTException; 
import java.awt.Frame; 
import java.awt.GraphicsEnvironment; 
import java.awt.Robot; 
import java.awt.TextField; 
import java.awt.event.KeyEvent; 
import java.awt.event.TextListener; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 

/** 
 * @test 
 * @summary Regression test for: Press&Hold macOS feature doesn't work when holding down an accent key using Robot 
 * @requires (jdk.version.major >= 8 & os.family == "mac") 
 * @run main HoldDownAccentKey 
 */ 

/* 
 * Description: Tests that accents popup menu appears instead of the key repeat 
 * when holding down an accent key with Java Robot if ApplePressAndHoldEnabled=1. 
 * 
 * Note: Test works with English keyboard layout. 
 * Test requires macOS system property ApplePressAndHoldEnabled=1 (default value for macOS >= 10.7). 
 * MacOS accessibility permission should also be granted for the application launching this test, so 
 * Java Robot is able to access keyboard (use System Preferences -> Security&Privacy -> Privacy tab -> Accessibility). 
 */ 

public class HoldDownAccentKey { 

    private static final int SAMPLE_KEY = KeyEvent.VK_A; 
    private static final int REPEAT_NUM = 10; 

 	private static final String SAMPLE_RESULT="à”;
	// private static final String SAMPLE_RESULT="\u00E0"; // à
    private static final String SAMPLE_REPEAT = "aaaaaaaaaa1"; 

    private static final int PAUSE = 2000; 

    private static volatile String result=""; 

    /* 
     * Returns macOS major and minor version as an integer 
     */ 
    private static int getMajorMinorMacOsVersion() { 
        int version = 0; 
        String versionProp = System.getProperty("os.version"); 
        if (versionProp != null && !versionProp.isEmpty()) { 
            String[] versionComponents = versionProp.split("\\."); 
            String majorMinor = versionComponents[0]; 
            if (versionComponents.length > 1) { 
                majorMinor += versionComponents[1]; 
            } 
            try { 
                version = Integer.parseInt(majorMinor); 
            } catch (NumberFormatException nfexception) { 
                // Do nothing 
            } 
        } 
        return version; 
    } 

    /* 
     * Returns ApplePressAndHoldEnabled system property value 
     */ 
    private static String getApplePressAndHoldValue() throws IOException, InterruptedException { 
        Process readDefaults = new ProcessBuilder("defaults", "read", "-g", "ApplePressAndHoldEnabled") 
                .redirectError(ProcessBuilder.Redirect.INHERIT).start(); 
        readDefaults.waitFor(); 

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(readDefaults.getInputStream()))) { 
            return reader.readLine(); 
        } 
    } 

    /* 
     * Checks that accents popup menu appears instead of the key repeat 
     * when holding down an accent key with Java Robot if ApplePressAndHoldEnabled=1 
     */ 
    public static void main(String[] args) throws AWTException, InterruptedException, IOException { 
        if (GraphicsEnvironment.isHeadless()) { 
            throw new RuntimeException("ERROR: Cannot execute the test in headless environment"); 
        } 

        final int osVersion = getMajorMinorMacOsVersion(); 
        if (osVersion == 0) { 
            throw new RuntimeException("ERROR: Cannot determine MacOS version"); 
        } else if (osVersion < 107) { 
            System.out.println("TEST SKIPPED: No Press&Hold feature for Snow Leopard or lower MacOS version"); 
            return; 
        } 

        final String applePressAndHoldValue = getApplePressAndHoldValue(); 
        System.out.println("ApplePressAndHoldEnabled = " + applePressAndHoldValue); 
        if (!"1".equals(applePressAndHoldValue)) { 
            throw new RuntimeException("TEST ERROR: ApplePressAndHoldEnabled system property must be set to 1"); 
        } 

        final Frame frame = new Frame("Test Frame"); 
        final TextField textField = new TextField(); 
        final TextListener textListener = (e -> result = textField.getText()); 

        try { 
            textField.addTextListener(textListener); 
            frame.add(textField); 
            frame.setSize(400, 200); 
            frame.setLocation(100, 100); 
            frame.setVisible(true); 

            Robot robot = new Robot(); 
            robot.setAutoDelay(50); 
            robot.waitForIdle(); 

            // Hold down sample key so accents popup menu may appear 
            for (int i = 0; i < REPEAT_NUM; i++) { 
                robot.keyPress(SAMPLE_KEY); 
            } 
            robot.keyRelease(SAMPLE_KEY); 

            // Select the first accent 
            robot.keyPress(KeyEvent.VK_1); 
            robot.keyRelease(KeyEvent.VK_1); 

            Thread.sleep(PAUSE); 
            robot.waitForIdle(); 

            if (SAMPLE_RESULT.equals(result)) { 
                System.out.println("TEST PASSED"); 
            } else if (SAMPLE_REPEAT.equals(result)) { 
                throw new RuntimeException("TEST FAILED: Holding a key down " + 
                        "causes the key repeat instead of accent menu popup"); 
            } else { 
                throw new RuntimeException("TEST ERROR: Unexpected input value: " + result); 
            } 

        } finally { 
            textField.removeTextListener(textListener); 
            frame.dispose(); 
            /* Waiting for EDT auto-shutdown */ 
            Thread.sleep(PAUSE); 
        } 
    } 
} 
