|  | 
|  | 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. | 
|  | 2 | +// Licensed under the MIT License. | 
|  | 3 | + | 
|  | 4 | +'use strict'; | 
|  | 5 | + | 
|  | 6 | +import { ChildProcess, spawn, SpawnOptions } from 'child_process'; | 
|  | 7 | +import * as fs from 'fs-extra'; | 
|  | 8 | +import { createServer, Server } from 'net'; | 
|  | 9 | +import * as path from 'path'; | 
|  | 10 | +import { EXTENSION_ROOT_DIR } from '../client/constants'; | 
|  | 11 | +import { noop, sleep } from './core'; | 
|  | 12 | + | 
|  | 13 | +// tslint:disable:no-console | 
|  | 14 | + | 
|  | 15 | +/* | 
|  | 16 | +This is a simple work around for tests tasks not completing on Azure Pipelines. | 
|  | 17 | +What's been happening is, the tests run however for some readon the Node propcess (VS Code) does not exit. | 
|  | 18 | +Here's what we've tried thus far: | 
|  | 19 | +* Dispose all timers | 
|  | 20 | +* Close all open streams/sockets. | 
|  | 21 | +* Use `process.exit` and use the VSC commands to close itself. | 
|  | 22 | +
 | 
|  | 23 | +Final solution: | 
|  | 24 | +* Start a node.js procecss | 
|  | 25 | + * This process will start a socket server | 
|  | 26 | + * This procecss will start the tests in a separate procecss (spawn) | 
|  | 27 | +* When the tests have completed, | 
|  | 28 | + * Send a message to the socket server with a flag (true/false whether tests passed/failed) | 
|  | 29 | +* Socket server (main procecss) will receive the test status flag. | 
|  | 30 | + * This will kill the spawned process | 
|  | 31 | + * This main process will kill itself with exit code 0 if tests pass succesfully, else 1. | 
|  | 32 | +*/ | 
|  | 33 | + | 
|  | 34 | +const testFile = process.argv[2]; | 
|  | 35 | +const portFile = path.join(EXTENSION_ROOT_DIR, 'port.txt'); | 
|  | 36 | + | 
|  | 37 | +let proc: ChildProcess | undefined; | 
|  | 38 | +let server: Server | undefined; | 
|  | 39 | + | 
|  | 40 | +async function deletePortFile() { | 
|  | 41 | + try { | 
|  | 42 | + if (await fs.pathExists(portFile)) { | 
|  | 43 | + await fs.unlink(portFile); | 
|  | 44 | + } | 
|  | 45 | + } catch { | 
|  | 46 | + noop(); | 
|  | 47 | + } | 
|  | 48 | +} | 
|  | 49 | +async function end(exitCode: number) { | 
|  | 50 | + if (exitCode === 0) { | 
|  | 51 | + console.log('Exiting without errors'); | 
|  | 52 | + } else { | 
|  | 53 | + console.error('Exiting with test failures'); | 
|  | 54 | + } | 
|  | 55 | + if (proc) { | 
|  | 56 | + try { | 
|  | 57 | + const procToKill = proc; | 
|  | 58 | + proc = undefined; | 
|  | 59 | + console.log('Killing VSC'); | 
|  | 60 | + await deletePortFile(); | 
|  | 61 | + // Wait for the std buffers to get flushed before killing. | 
|  | 62 | + await sleep(5_000); | 
|  | 63 | + procToKill.kill(); | 
|  | 64 | + } catch { | 
|  | 65 | + noop(); | 
|  | 66 | + } | 
|  | 67 | + } | 
|  | 68 | + if (server) { | 
|  | 69 | + server.close(); | 
|  | 70 | + } | 
|  | 71 | + // Exit with required code. | 
|  | 72 | + process.exit(exitCode); | 
|  | 73 | +} | 
|  | 74 | + | 
|  | 75 | +async function startSocketServer() { | 
|  | 76 | + return new Promise(resolve => { | 
|  | 77 | + server = createServer(socket => { | 
|  | 78 | + socket.on('data', buffer => { | 
|  | 79 | + const data = buffer.toString('utf8'); | 
|  | 80 | + console.log(`Exit code from Tests is ${data}`); | 
|  | 81 | + const code = parseInt(data.substring(0, 1), 10); | 
|  | 82 | + end(code).catch(noop); | 
|  | 83 | + }); | 
|  | 84 | + }); | 
|  | 85 | + | 
|  | 86 | + server.listen({ host: '127.0.0.1', port: 0 }, async () => { | 
|  | 87 | + const port = server!.address().port; | 
|  | 88 | + console.log(`Test server listening on port ${port}`); | 
|  | 89 | + await deletePortFile(); | 
|  | 90 | + await fs.writeFile(portFile, port.toString()); | 
|  | 91 | + resolve(); | 
|  | 92 | + }); | 
|  | 93 | + }); | 
|  | 94 | +} | 
|  | 95 | + | 
|  | 96 | +async function start() { | 
|  | 97 | + await startSocketServer(); | 
|  | 98 | + const options: SpawnOptions = { cwd: process.cwd(), env: process.env, detached: true, stdio: 'inherit' }; | 
|  | 99 | + proc = spawn(process.execPath, [testFile], options); | 
|  | 100 | + proc.once('close', end); | 
|  | 101 | +} | 
|  | 102 | + | 
|  | 103 | +start().catch(ex => console.error(ex)); | 
0 commit comments