Skip to content

Commit 953f370

Browse files
Wait for port to be freed before continuing
1 parent 200d806 commit 953f370

File tree

3 files changed

+49
-5
lines changed

3 files changed

+49
-5
lines changed

test/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"chai": "^3.5.0",
1919
"cross-spawn": "^5.0.1",
2020
"mkdirp": "^0.5.1",
21+
"portastic": "^1.0.1",
2122
"rimraf": "^2.5.4",
2223
"selenium-standalone": "^5.9.0",
2324
"tree-kill": "^1.1.0",

test/templates/util/aspnet.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import * as childProcess from 'child_process';
22
import * as path from 'path';
33
import * as readline from 'readline';
4+
import { waitUntilPortState } from './ports';
45
const treeKill = require('tree-kill');
56
const crossSpawn: typeof childProcess.spawn = require('cross-spawn');
7+
const defaultPort = 5000;
68

7-
export const defaultUrl = 'http://localhost:5000';
9+
export const defaultUrl = `http://localhost:${ defaultPort }`;
810

911
export enum AspNetCoreEnviroment {
1012
development,
@@ -50,7 +52,7 @@ export class AspNetProcess {
5052
});
5153

5254
// Ensure the process isn't orphaned even if Node crashes before we're disposed
53-
process.on('exit', () => this._killProcessSync());
55+
process.on('exit', () => this._killAspNetProcess());
5456

5557
// Also track whether it exited on its own already
5658
this._process.on('exit', () => {
@@ -74,7 +76,7 @@ export class AspNetProcess {
7476

7577
public dispose(): Promise<any> {
7678
return new Promise((resolve, reject) => {
77-
this._killProcessSync(err => {
79+
this._killAspNetProcess(err => {
7880
if (err) {
7981
reject(err);
8082
} else {
@@ -84,11 +86,29 @@ export class AspNetProcess {
8486
});
8587
}
8688

87-
private _killProcessSync(callback?: (err: any) => void) {
89+
private _killAspNetProcess(callback?: (err: any) => void) {
90+
callback = callback || (() => {});
8891
if (!this._processHasExited) {
8992
// It's important to kill the whole tree, because 'dotnet run' launches a separate 'dotnet exec'
9093
// child process that would otherwise be left running
91-
treeKill(this._process.pid, 'SIGINT', callback);
94+
treeKill(this._process.pid, 'SIGINT', err => {
95+
if (err) {
96+
callback(err);
97+
} else {
98+
// It's not enough just to send a SIGINT to ASP.NET. It will stay open for a moment, completing
99+
// any outstanding requests. We have to wait for it really to be gone before continuing, otherwise
100+
// the next test might be unable to start because of the port still being in use.
101+
console.log(`Waiting until port ${ defaultPort } is closed...`);
102+
waitUntilPortState(defaultPort, /* isOpen */ true, /* timeoutMs */ 15000, err => {
103+
if (err) {
104+
callback(err);
105+
} else {
106+
console.log(`Port ${ defaultPort } is now closed`);
107+
callback(null);
108+
}
109+
});
110+
}
111+
});
92112
}
93113
}
94114
}

test/templates/util/ports.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as portastic from 'portastic';
2+
const pollInterval = 500;
3+
4+
export function waitUntilPortState(port: number, isOpen: boolean, timeoutMs: number, callback: (err: any) => void) {
5+
if (!(timeoutMs > 0)) {
6+
throw new Error(`Timed out after ${ timeoutMs }ms waiting for port ${ port } to become ${ isOpen ? 'free' : 'in use' }`);
7+
}
8+
9+
portastic.test(port).then(
10+
actualIsOpenState => {
11+
if (actualIsOpenState === isOpen) {
12+
// Desired state is reached
13+
callback(null);
14+
} else {
15+
// Wait longer
16+
setTimeout(() => {
17+
waitUntilPortState(port, isOpen, timeoutMs - pollInterval, callback);
18+
}, pollInterval);
19+
}
20+
},
21+
callback
22+
)
23+
}

0 commit comments

Comments
 (0)