Skip to content

Commit

Permalink
Add a test that builds a Windows binary remotely and runs it locally,…
Browse files Browse the repository at this point in the history
… 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 bazelbuild#4962.
  • Loading branch information
Jeremy Sharpe committed Apr 9, 2018
1 parent eb0671b commit dcbf1e4
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 0 deletions.
18 changes: 18 additions & 0 deletions src/test/py/bazel/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ filegroup(
name = "test-deps",
testonly = 1,
srcs = ["//src:bazel_with_jdk"],
data = ["//src/tools/remote:worker"],
)

py_library(
Expand Down Expand Up @@ -84,6 +85,23 @@ 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",
}),
deps = select({
"//src/conditions:windows": [":test_base"],
"//conditions:default": [],
}),
)

py_test(
name = "launcher_test",
size = "medium",
Expand Down
70 changes: 70 additions & 0 deletions src/test/py/bazel/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import locale
import os
import socket
import stat
import subprocess
import tempfile
Expand Down Expand Up @@ -44,6 +45,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)
Expand Down Expand Up @@ -219,6 +223,69 @@ def RunBazel(self, args, env_remove=None, env_add=None):
'--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)
work_dir = tempfile.mkdtemp(dir=self._test_cwd)

# 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=C:\\tmp\\lre',
],
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.
Expand Down Expand Up @@ -263,6 +330,9 @@ def _EnvMap(self, env_remove=None, env_add=None):
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'),
Expand Down
82 changes: 82 additions & 0 deletions src/test/py/bazel/windows_remote_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# pylint: disable=g-bad-file-header
# 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):

_worker_stdout = None
_worker_stderr = None

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"],',
')',
])
foo_sh = self.ScratchFile('foo/foo.sh', [
'#!/bin/bash',
'echo hello shell',
])

exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-bin'])
self.AssertExitCode(exit_code, 0, stderr)
bazel_bin = stdout[0]

port = self.StartRemoteWorker()

# Build.
exit_code, stdout, stderr = self._RunRemoteBazel(['build', '//foo:foo'], port)
print 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'])

self.StopRemoteWorker()

if __name__ == '__main__':
unittest.main()

0 comments on commit dcbf1e4

Please sign in to comment.