test/integration/goDebug: speed up setUpRemoteProgram

Instead of sleeping for fixed 10sec, watch the stdout
and look for the server start signature message.

Either Delve's start message (if --continue flag is not used)
   i.e., DAP server listening at: or API server listening at:
or the helloWorldServer's log message (if --continue flag is used)
   helloWorldServer starting to listen on

The logic is inspired from the current debug adapter's
dlv spawning logic.

In most cases, this approach makes tests complete fast.

If server takes more than 10sec to start up, this
approach is more robust as long as the server setup can be done
within hard 30sec limit.

Change-Id: Ie880bdf50cb33e75873e2d8fb4e92ca9c53e633f
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/418896
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
diff --git a/test/integration/goDebug.test.ts b/test/integration/goDebug.test.ts
index 99d6594..4fcd372 100644
--- a/test/integration/goDebug.test.ts
+++ b/test/integration/goDebug.test.ts
@@ -413,14 +413,45 @@
 		if (continueOnStart) {
 			args.push('--continue');
 		}
-		const childProcess = cp.spawn(toolPath, args, {
-			cwd: serverFolder,
-			env: { PORT: `${serverPort}`, ...process.env }
-		});
 
-		// Give dlv a few seconds to start.
-		await new Promise((resolve) => setTimeout(resolve, 10_000));
-		return childProcess;
+		const promise = new Promise<cp.ChildProcess>((resolve, reject) => {
+			const p = cp.spawn(toolPath, args, {
+				cwd: serverFolder,
+				env: { PORT: `${serverPort}`, ...process.env }
+			});
+
+			let started = false;
+			const timeoutToken: NodeJS.Timer = setTimeout(() => {
+				console.log(`dlv debug server (PID: ${p.pid}) is not responding`);
+				reject(new Error('timed out while waiting for DAP server to start'));
+			}, 30_000);
+
+			const stopWaitingForServerToStart = () => {
+				clearTimeout(timeoutToken);
+				started = true;
+				resolve(p);
+			};
+
+			if (continueOnStart) {
+				// wait till helloWorldServer starts and prints its log message to STDERR.
+				p.stderr.on('data', (chunk) => {
+					const msg = chunk.toString();
+					if (!started && msg.includes('helloWorldServer starting to listen on')) {
+						stopWaitingForServerToStart();
+					}
+					console.log(msg);
+				});
+			} else {
+				p.stdout.on('data', (chunk) => {
+					const msg = chunk.toString();
+					if (!started && msg.includes('listening at:')) {
+						stopWaitingForServerToStart();
+					}
+					console.log(msg);
+				});
+			}
+		});
+		return promise;
 	}
 
 	/**
diff --git a/test/testdata/helloWorldServer/main.go b/test/testdata/helloWorldServer/main.go
index a1598af..ff35e27 100644
--- a/test/testdata/helloWorldServer/main.go
+++ b/test/testdata/helloWorldServer/main.go
@@ -16,8 +16,8 @@
 	if p := os.Getenv("PORT"); p != "" {
 		addr = ":" + p
 	}
-
-	log.Printf("server starting to listen on %s", addr)
+	// NOTE: goDebug.test.ts setupRemoteProgram expects this log message.
+	log.Printf("helloWorldServer starting to listen on %s", addr)
 	http.HandleFunc("/", home)
 	if err := http.ListenAndServe(addr, nil); err != nil {
 		log.Fatalf("server listen error: %+v", err)
@@ -26,6 +26,6 @@
 
 // home logs the received request and returns a simple response.
 func home(w http.ResponseWriter, r *http.Request) {
-	log.Printf("received request: %s %s", r.Method, r.URL.Path)
+	log.Printf("received request: %s %s", r.Method, r.URL.Path) // Breakpoint
 	fmt.Fprintf(w, "Hello, world!")
 }