diff options
author | Googler <noreply@google.com> | 2018-04-11 07:06:28 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-04-11 07:07:41 -0700 |
commit | 6a5b0772c6e5fedc912949663de8b79503df5e78 (patch) | |
tree | ac99c34a085126310097552bbbb0fe08e60b793b /src/test/py | |
parent | da32f42b75de137b1302632d1dbbb0b1e281ac53 (diff) |
Add a test that builds a Windows binary remotely and runs it locally, to validate this key use case for remote Windows builds.
This is a working test case, but I plan to add more in this style to demonstrate fixes to address #4962.
Note that to run this test requires a build of Bazel that includes https://github.com/bazelbuild/bazel/commit/b4545ba2b1aa4079b09a346a6d441ffa1e1b7d20 since this changed the way runfile manifests are discovered. Bazel 0.11 doesn't include this.
RELNOTES: None.
PiperOrigin-RevId: 192444770
Diffstat (limited to 'src/test/py')
-rw-r--r-- | src/test/py/bazel/BUILD | 19 | ||||
-rw-r--r-- | src/test/py/bazel/test_base.py | 75 | ||||
-rw-r--r-- | src/test/py/bazel/windows_remote_test.py | 88 |
3 files changed, 182 insertions, 0 deletions
diff --git a/src/test/py/bazel/BUILD b/src/test/py/bazel/BUILD index fd79511fd7..c2b7f01a65 100644 --- a/src/test/py/bazel/BUILD +++ b/src/test/py/bazel/BUILD @@ -10,6 +10,7 @@ filegroup( name = "test-deps", testonly = 1, srcs = ["//src:bazel_with_jdk"], + data = ["//src/tools/remote:worker"], ) py_library( @@ -85,6 +86,24 @@ py_test( ) py_test( + name = "windows_remote_test", + size = "medium", + srcs = select({ + "//src/conditions:windows": ["windows_remote_test.py"], + "//conditions:default": ["empty_test.py"], + }), + main = select({ + "//src/conditions:windows": "windows_remote_test.py", + "//conditions:default": "empty_test.py", + }), + tags = ["manual"], # TODO(jsharpe): Reenable once Bazel 0.12 is used on CI. + deps = select({ + "//src/conditions:windows": [":test_base"], + "//conditions:default": [], + }), +) + +py_test( name = "launcher_test", size = "medium", srcs = ["launcher_test.py"], diff --git a/src/test/py/bazel/test_base.py b/src/test/py/bazel/test_base.py index 27d1898ab7..345e3414ed 100644 --- a/src/test/py/bazel/test_base.py +++ b/src/test/py/bazel/test_base.py @@ -1,4 +1,5 @@ # pylint: disable=g-bad-file-header +# pylint: disable=superfluous-parens # Copyright 2017 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,6 +16,7 @@ import locale import os +import socket import stat import subprocess import tempfile @@ -44,6 +46,9 @@ class TestBase(unittest.TestCase): _temp = None _tests_root = None _test_cwd = None + _worker_stdout = None + _worker_stderr = None + _worker_proc = None def setUp(self): unittest.TestCase.setUp(self) @@ -219,6 +224,73 @@ class TestBase(unittest.TestCase): '--nomaster_bazelrc', ] + args, env_remove, env_add) + def StartRemoteWorker(self): + """Runs a "local remote worker" to run remote builds and tests on. + + Returns: + int: port that the local remote worker runs on. + """ + self._worker_stdout = tempfile.TemporaryFile(dir=self._test_cwd) + self._worker_stderr = tempfile.TemporaryFile(dir=self._test_cwd) + # Ideally we would use something under TEST_TMPDIR here, but the + # worker path must be as short as possible so we don't exceed Windows + # path length limits, so we run straight in TEMP. This should ideally + # be set to something like C:\temp. On CI this is set to D:\temp. + worker_path = TestBase.GetEnv('TEMP') + + # Get an open port. Unfortunately this seems to be the best option in + # Python. + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.bind(('', 0)) + port = s.getsockname()[1] + s.close() + + # Tip: To help debug remote build problems, add the --debug flag below. + self._worker_proc = subprocess.Popen( + [ + self.Rlocation('io_bazel/src/tools/remote/worker.exe'), + '--listen_port=' + str(port), + # This path has to be extremely short to avoid Windows path + # length restrictions. + '--work_path=' + worker_path, + ], + stdout=self._worker_stdout, + stderr=self._worker_stderr, + cwd=self._test_cwd, + env=self._EnvMap(env_add={ + 'RUNFILES_MANIFEST_FILE': TestBase.GetEnv('RUNFILES_MANIFEST_FILE'), + })) + + return port + + def StopRemoteWorker(self): + """Stop the "local remote worker" started by StartRemoteWorker. + + Prints its stdout and stderr out for debug purposes. + """ + self._worker_proc.terminate() + self._worker_proc.wait() + + self._worker_stdout.seek(0) + stdout_lines = [ + l.decode(locale.getpreferredencoding()).strip() + for l in self._worker_stdout.readlines() + ] + if stdout_lines: + print('Local remote worker stdout') + print('--------------------------') + print('\n'.join(stdout_lines)) + + self._worker_stderr.seek(0) + stderr_lines = [ + l.decode(locale.getpreferredencoding()).strip() + for l in self._worker_stderr.readlines() + ] + if stderr_lines: + print('Local remote worker stderr') + print('--------------------------') + print('\n'.join(stderr_lines)) + def RunProgram(self, args, env_remove=None, env_add=None): """Runs a program (args[0]), waits for it to exit. @@ -263,6 +335,9 @@ class TestBase(unittest.TestCase): TestBase.GetEnv('SYSTEMROOT'), # TODO(laszlocsomor): Let Bazel pass BAZEL_SH to tests and use that # here instead of hardcoding paths. + # + # You can override this with + # --action_env=BAZEL_SH=C:\path\to\my\bash.exe. 'BAZEL_SH': TestBase.GetEnv('BAZEL_SH', 'c:\\tools\\msys64\\usr\\bin\\bash.exe'), diff --git a/src/test/py/bazel/windows_remote_test.py b/src/test/py/bazel/windows_remote_test.py new file mode 100644 index 0000000000..11b6d0d824 --- /dev/null +++ b/src/test/py/bazel/windows_remote_test.py @@ -0,0 +1,88 @@ +# pylint: disable=g-bad-file-header +# pylint: disable=superfluous-parens +# Copyright 2017 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import unittest +from src.test.py.bazel import test_base + + +class WindowsRemoteTest(test_base.TestBase): + + def _RunRemoteBazel(self, args, port, env_remove=None, env_add=None): + return self.RunBazel( + args + [ + '--spawn_strategy=remote', + '--strategy=Javac=remote', + '--strategy=Closure=remote', + '--genrule_strategy=remote', + '--define=EXECUTOR=remote', + '--remote_executor=localhost:' + str(port), + '--remote_cache=localhost:' + str(port), + '--experimental_strict_action_env=true', + '--remote_timeout=3600', + '--auth_enabled=false', + '--remote_accept_cached=false', + ], + env_remove=env_remove, + env_add=env_add) + + # Check that a binary built remotely is runnable locally. Among other things, + # this means the runfiles manifest, which is not present remotely, must exist + # locally. + def testBinaryRunnableLocally(self): + self.ScratchFile('WORKSPACE') + self.ScratchFile('foo/BUILD', [ + 'sh_binary(', + ' name = "foo",', + ' srcs = ["foo.sh"],', + ' data = ["//bar:bar.txt"],', + ')', + ]) + self.ScratchFile( + 'foo/foo.sh', [ + '#!/bin/bash', + 'echo hello shell', + ], executable=True) + self.ScratchFile('bar/BUILD', ['exports_files(["bar.txt"])']) + self.ScratchFile('bar/bar.txt', ['hello']) + + exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-bin']) + self.AssertExitCode(exit_code, 0, stderr) + bazel_bin = stdout[0] + + port = self.StartRemoteWorker() + + try: + # Build. + exit_code, stdout, stderr = self._RunRemoteBazel(['build', '//foo:foo'], + port) + print('\n'.join(stdout)) + self.AssertExitCode(exit_code, 0, stderr) + + # Run. + foo_bin = os.path.join(bazel_bin, 'foo', 'foo.exe') + self.assertTrue(os.path.exists(foo_bin)) + exit_code, stdout, stderr = self.RunProgram([foo_bin]) + self.AssertExitCode(exit_code, 0, stderr) + self.assertEqual(stdout, ['hello shell']) + finally: + # Always stop the worker so we obtain logs in case an assertion failed + # above. + self.StopRemoteWorker() + + +if __name__ == '__main__': + unittest.main() |