From 9de9c8ad0301f5008d1d08f4ddcc280e7398b7af Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Thu, 3 Jan 2019 20:25:51 +0100 Subject: [PATCH] fix(bazel): protractor utils cannot start server on windows (#27915) * Currently the protractor utils assume that the specified Bazel server runfile can be resolved by just using the real file system. This is not the case on Windows because the runfiles are not symlinked into the working directory and need to be resolved through the runfile manifest. PR Close #27915 --- packages/bazel/src/protractor/utils/index.ts | 39 +++++++++++-------- .../bazel/test/protractor-utils/BUILD.bazel | 29 ++++++++++++++ .../test/protractor-utils/fake-devserver.js | 23 +++++++++++ .../bazel/test/protractor-utils/index_test.ts | 17 ++++++++ 4 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 packages/bazel/test/protractor-utils/BUILD.bazel create mode 100644 packages/bazel/test/protractor-utils/fake-devserver.js create mode 100644 packages/bazel/test/protractor-utils/index_test.ts diff --git a/packages/bazel/src/protractor/utils/index.ts b/packages/bazel/src/protractor/utils/index.ts index 666119d516..424d2a21bb 100644 --- a/packages/bazel/src/protractor/utils/index.ts +++ b/packages/bazel/src/protractor/utils/index.ts @@ -71,24 +71,31 @@ export interface ServerSpec { port: number; } -export function runServer( - workspace: string, binary: string, portFlag: string, args: string[], +/** + * Runs the specified server binary from a given workspace and waits for the server + * being ready. The server binary will be resolved from the Bazel runfiles. Note that + * the server will be launched with a random free port in order to support test concurrency + * with Bazel. + */ +export async function runServer( + workspace: string, serverTarget: string, portFlag: string, serverArgs: string[], timeout = 5000): Promise { - return findFreeTcpPort().then(function(port) { - const runfiles_path = process.env.TEST_SRCDIR; - const cmd = path.join(runfiles_path, workspace, binary); + const serverPath = require.resolve(`${workspace}/${serverTarget}`); + const port = await findFreeTcpPort(); - args = args.concat([portFlag, port.toString()]); + // Start the Bazel server binary with a random free TCP port. + const serverProcess = child_process.spawn( + serverPath, serverArgs.concat([portFlag, port.toString()]), {stdio: 'inherit'}); - const child = child_process.spawn( - cmd, args, {cwd: path.join(runfiles_path, workspace), stdio: 'inherit'}); - - child.on('exit', function(code) { - if (code != 0) { - throw new Error(`non-zero exit code ${code} from server`); - } - }); - - return waitForServer(port, timeout).then(() => { return {port}; }); + // In case the process exited with an error, we want to propagate the error. + serverProcess.on('exit', exitCode => { + if (exitCode !== 0) { + throw new Error(`Server exited with error code: ${exitCode}`); + } }); + + // Wait for the server to be bound to the given port. + await waitForServer(port, timeout); + + return {port}; } diff --git a/packages/bazel/test/protractor-utils/BUILD.bazel b/packages/bazel/test/protractor-utils/BUILD.bazel new file mode 100644 index 0000000000..33c2d77f4f --- /dev/null +++ b/packages/bazel/test/protractor-utils/BUILD.bazel @@ -0,0 +1,29 @@ +load("//tools:defaults.bzl", "jasmine_node_test", "nodejs_binary", "ts_library") + +ts_library( + name = "protractor_utils_tests_lib", + testonly = True, + srcs = ["index_test.ts"], + deps = [ + "//packages/bazel/src/protractor/utils", + ], +) + +nodejs_binary( + name = "fake-devserver", + testonly = True, + data = [ + "fake-devserver.js", + "@ngdeps//minimist", + ], + entry_point = "angular/packages/bazel/test/protractor-utils/fake-devserver.js", +) + +jasmine_node_test( + name = "protractor_utils_tests", + size = "small", + srcs = [":protractor_utils_tests_lib"], + data = [ + ":fake-devserver", + ], +) diff --git a/packages/bazel/test/protractor-utils/fake-devserver.js b/packages/bazel/test/protractor-utils/fake-devserver.js new file mode 100644 index 0000000000..34e84d3a07 --- /dev/null +++ b/packages/bazel/test/protractor-utils/fake-devserver.js @@ -0,0 +1,23 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +const http = require('http'); +const minimist = require('minimist'); + +const {port} = minimist(process.argv); +const server = new http.Server(); + +// Basic request handler so that it could respond to fake requests. +server.on('request', (req, res) => { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Running'); +}); + +server.listen(port); + +console.info('Server running on port:', port); diff --git a/packages/bazel/test/protractor-utils/index_test.ts b/packages/bazel/test/protractor-utils/index_test.ts new file mode 100644 index 0000000000..1bc0d97731 --- /dev/null +++ b/packages/bazel/test/protractor-utils/index_test.ts @@ -0,0 +1,17 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {runServer} from '../../src/protractor/utils'; + +describe('Bazel protractor utils', () => { + + it('should be able to start devserver', async() => { + // Test will automatically time out if the server couldn't be launched as expected. + await runServer('angular', 'packages/bazel/test/protractor-utils/fake-devserver', '--port', []); + }); +});