1+ package io.openliberty.tools.gradle
2+
3+ import java.util.concurrent.TimeUnit ;
4+
5+ import static org.junit.Assert.*;
6+
7+ import org.apache.commons.io.FileUtils ;
8+ import org.junit.After ;
9+ import org.junit.AfterClass ;
10+ import org.junit.BeforeClass ;
11+ import org.junit.Test ;
12+
13+ /**
14+ * Test early quit functionality during dev mode startup for issue #1638.
15+ * These tests verify that users can press 'q' to quit during server startup,
16+ * and that other hotkeys are properly restricted during startup.
17+ */
18+ class DevEarlyQuitTest extends BaseDevTest {
19+ static final String projectName = " basic-dev-project" ;
20+
21+ static File resourceDir = new File (" build/resources/test/dev-test/" + projectName);
22+ static File buildDir = new File (integTestDir, " dev-early-quit-test/" + projectName + System . currentTimeMillis());
23+
24+ @BeforeClass
25+ public static void setup () throws IOException , InterruptedException , FileNotFoundException {
26+ createDir(buildDir);
27+ createTestProject(buildDir, resourceDir, buildFilename);
28+ }
29+
30+ @After
31+ public void cleanupAfterEachTest () throws Exception {
32+ // Ensure cleanup after each test
33+ if (process != null && process. isAlive()) {
34+ process. destroy();
35+ process. waitFor(10 , TimeUnit . SECONDS );
36+ }
37+ }
38+
39+ @AfterClass
40+ public static void tearDown () throws Exception {
41+ // Clean up process if still running
42+ if (process != null && process. isAlive()) {
43+ process. destroy();
44+ process. waitFor(30 , TimeUnit . SECONDS );
45+ }
46+
47+ // Clean up build directory
48+ if (buildDir != null && buildDir. exists()) {
49+ FileUtils . deleteQuietly(buildDir);
50+ }
51+ }
52+
53+ /**
54+ * Helper method to wait for log file to be created and process to be ready
55+ */
56+ private static boolean waitForDevModeToInitialize (int timeoutSeconds ) {
57+ long startTime = System . currentTimeMillis();
58+ long timeout = timeoutSeconds * 1000 ;
59+
60+ // Wait for log file to exist and have some content
61+ while (System . currentTimeMillis() - startTime < timeout) {
62+ if (logFile. exists() && logFile. length() > 0 && process. isAlive()) {
63+ // Give it a bit more time to ensure keyboard listener is ready
64+ try {
65+ Thread . sleep(3000 );
66+ } catch (InterruptedException e) {
67+ Thread . currentThread(). interrupt();
68+ }
69+ return true ;
70+ }
71+ try {
72+ Thread . sleep(500 );
73+ } catch (InterruptedException e) {
74+ Thread . currentThread(). interrupt();
75+ return false ;
76+ }
77+ }
78+ return false ;
79+ }
80+
81+ @Test
82+ public void testEarlyQuitDuringStartup () throws Exception {
83+ // Start dev mode without waiting for full startup
84+ startDevModeWithoutWaiting(buildDir);
85+
86+ // Wait for dev mode to initialize (log file created, process alive)
87+ boolean devModeStarted = waitForDevModeToInitialize(30 );
88+ assertTrue (" Dev mode should have started" , devModeStarted);
89+
90+ // Verify process is still running
91+ assertTrue (" Process should be alive" , process. isAlive());
92+
93+ // Send 'q' to quit DURING startup (before server fully starts)
94+ System . out. println (" Sending 'q' to quit during startup..." );
95+ writer. write(" q" );
96+ writer. newLine();
97+ writer. flush();
98+
99+ // Wait for process to terminate
100+ boolean terminated = process. waitFor(90 , TimeUnit . SECONDS );
101+ assertTrue (" Process should have terminated after 'q' during startup" , terminated);
102+
103+ // Verify process is no longer alive
104+ assertFalse (" Process should not be alive after early quit" , process. isAlive());
105+
106+ System . out. println (" Early quit during startup test passed" );
107+ }
108+
109+ @Test
110+ public void testOtherHotkeysIgnoredDuringStartup () throws Exception {
111+ // Start dev mode without waiting for full startup
112+ startDevModeWithoutWaiting(buildDir);
113+
114+ // Wait for dev mode to initialize
115+ boolean devModeStarted = waitForDevModeToInitialize(30 );
116+ assertTrue (" Dev mode should have started" , devModeStarted);
117+
118+ // Verify process is running
119+ assertTrue (" Process should be alive" , process. isAlive());
120+
121+ // Try various hotkeys that should be ignored during startup (excluding 'q' and 'h')
122+ String [] ignoredKeys = [" r" , " g" , " o" , " t" , " p" , " \n " ];
123+
124+ System . out. println (" Sending hotkeys that should be ignored during startup..." );
125+ for (String key : ignoredKeys) {
126+ writer. write(key);
127+ if (! key. equals(" \n " )) {
128+ writer. newLine();
129+ }
130+ writer. flush();
131+ Thread . sleep(300 );
132+ }
133+
134+ // Wait a bit for messages to be logged
135+ Thread . sleep(2000 );
136+
137+ // Process should still be alive (none of the commands should have quit)
138+ assertTrue (" Process should still be alive after ignored hotkeys" , process. isAlive());
139+
140+ // Check logs for the "command not available" message
141+ String logContent = logFile. exists() ? logFile. text : " " ;
142+ assertTrue (" Should have message about command not being available during startup" ,
143+ logContent. contains(" The requested command is not available during server startup" ));
144+
145+ // Verify that restart (r) didn't happen
146+ int restartCount = logContent. split(" Restarting" ). length - 1 ;
147+ assertEquals (" Should not have restarted during startup" , 0 , restartCount);
148+
149+ System . out. println (" Verified that other hotkeys are ignored during startup" );
150+
151+ // Now send 'q' to properly quit
152+ writer. write(" q" );
153+ writer. newLine();
154+ writer. flush();
155+
156+ boolean terminated = process. waitFor(90 , TimeUnit . SECONDS );
157+ assertTrue (" Process should have terminated after 'q' command" , terminated);
158+
159+ System . out. println (" Other hotkeys ignored during startup test passed" );
160+ }
161+
162+ @Test
163+ public void testHelpCommandDuringStartup () throws Exception {
164+ // Start dev mode without waiting for full startup
165+ startDevModeWithoutWaiting(buildDir);
166+
167+ // Wait for dev mode to initialize
168+ boolean devModeStarted = waitForDevModeToInitialize(30 );
169+ assertTrue (" Dev mode should have started" , devModeStarted);
170+
171+ // Verify process is running
172+ assertTrue (" Process should be alive" , process. isAlive());
173+
174+ // Send 'h' command to show help
175+ System . out. println (" Sending 'h' to show help during startup..." );
176+ writer. write(" h" );
177+ writer. newLine();
178+ writer. flush();
179+
180+ // Wait a bit for help to be displayed
181+ Thread . sleep(2000 );
182+
183+ // Process should still be alive (help shouldn't quit)
184+ assertTrue (" Process should still be alive after 'h' command" , process. isAlive());
185+
186+ System . out. println (" Verified that 'h' command works during startup" );
187+
188+ // Now send 'q' to properly quit
189+ writer. write(" q" );
190+ writer. newLine();
191+ writer. flush();
192+
193+ // Give time for graceful shutdown
194+ boolean terminated = process. waitFor(90 , TimeUnit . SECONDS );
195+ assertTrue (" Process should have terminated after 'q' command" , terminated);
196+
197+ System . out. println (" Help command during startup test passed" );
198+ }
199+ }
0 commit comments