Skip to content

Commit

Permalink
New bazel macro to test run installed drake.
Browse files Browse the repository at this point in the history
`install_test()` is a wrapper macro that creates a Python script that is run
during the testing phase. The Python script installs `drake` and runs the
targets that are listed in `install_tests` in install rules.
The Python script that is created will allow to test multiple targets in
the install tree without having to install drake multiple times.
`install_test()` should be called only once, in `//:*`.
All the install tests that have been created in all the `install()` commands
in the source tree will be integrated in the Python script that is generated.
The targets can either be executables that are installed, or any executable
or scripts that is designed to test drake features in the install tree.
The Python install_test_helper file (`tools/install/install_test_helper.py`)
has been improved to easily create Python scripts that run commands from the
install directory. This is convenient when trying to test an executable in
the install tree that is run with parameters, or an executable that needs to
be killed because it never stops running.
  • Loading branch information
Francois Budin committed Mar 2, 2018
1 parent c759a0b commit a7ba439
Show file tree
Hide file tree
Showing 21 changed files with 493 additions and 222 deletions.
11 changes: 10 additions & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# This file is named BUILD.bazel instead of the more typical BUILD, so that on
# OSX it won't conflict with a build artifacts directory named "build".

load("@drake//tools/install:install.bzl", "install")
load("@drake//tools/install:install.bzl", "install", "install_test")
load("//tools/lint:lint.bzl", "add_lint_tests")

package(
Expand Down Expand Up @@ -40,8 +40,11 @@ py_library(
srcs = ["__init__.py"],
)

_INSTALL_TEST_COMMANDS = "install_test_commands.py"

install(
name = "install",
install_tests_script = _INSTALL_TEST_COMMANDS,
docs = ["LICENSE.TXT"],
deps = [
"//automotive/models:install_data",
Expand All @@ -57,4 +60,10 @@ install(
],
)

install_test(
name = "install_test",
src = _INSTALL_TEST_COMMANDS,
data = [":install"],
)

add_lint_tests()
37 changes: 10 additions & 27 deletions bindings/pydrake/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ drake_py_library(

install(
name = "install",
install_tests = [
":test/all_install_test.py",
":test/common_install_test.py",
],
targets = PY_LIBRARIES + [":all_py"],
py_dest = PACKAGE_INFO.py_dest,
visibility = ["//visibility:public"],
Expand All @@ -156,17 +160,6 @@ drake_py_test(
deps = [":all_py"],
)

drake_py_test(
name = "all_install_test",
size = "medium",
# Increase the timeout so that debug builds are successful.
timeout = "long",
data = ["//:install"],
deps = [
"//tools/install:install_test_helper",
],
)

# Test ODR (One Definition Rule).
drake_pybind_library(
name = "odr_test_module_py",
Expand Down Expand Up @@ -199,21 +192,6 @@ drake_py_test(
deps = [":common_py"],
)

# `//:install` is run in this test to verify that once installed
# pydrake still works. This test is implemented in a separate file from
# common_test to be able to remove files in the sandbox without
# interfering with other tests.
drake_py_test(
name = "common_install_test",
size = "medium",
# Increase the timeout so that debug builds are successful.
timeout = "long",
data = ["//:install"],
deps = [
"//tools/install:install_test_helper",
],
)

drake_py_test(
name = "forward_diff_test",
size = "small",
Expand Down Expand Up @@ -261,4 +239,9 @@ drake_py_test(
],
)

add_lint_tests()
add_lint_tests(
python_lint_extra_srcs = [
":test/all_install_test.py",
":test/common_install_test.py",
],
)
15 changes: 8 additions & 7 deletions bindings/pydrake/test/all_install_test.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env python
"""
Ensures we can import `pydrake.all` from install.
"""

import os
import subprocess
import sys
import unittest

Expand All @@ -12,19 +12,20 @@

class TestAllInstall(unittest.TestCase):
def test_install(self):
# Install into a temporary directory.
tmp_folder = "tmp"
result = install_test_helper.install(tmp_folder, ['lib'])
self.assertEqual(None, result)
# Get install directory.
install_dir = install_test_helper.get_install_dir()
# Override PYTHONPATH to only use the installed `pydrake` module.
env_python_path = "PYTHONPATH"
tool_env = dict(os.environ)
tool_env[env_python_path] = os.path.abspath(
os.path.join(tmp_folder, "lib", "python2.7", "site-packages")
os.path.join(install_dir, "lib", "python2.7", "site-packages")
)
# Ensure we can import all user-visible modules.
script = "import pydrake.all"
subprocess.check_call([sys.executable, "-c", script], env=tool_env)
install_test_helper.check_call(
[install_test_helper.get_python_executable(), "-c", script],
env=tool_env
)


if __name__ == '__main__':
Expand Down
30 changes: 10 additions & 20 deletions bindings/pydrake/test/common_install_test.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,42 +1,32 @@
import os
import shutil
import subprocess
import unittest
import sys
#!/usr/bin/env python

import os
import install_test_helper
import unittest


class TestCommonInstall(unittest.TestCase):
def testDrakeFindResourceOrThrowInInstall(self):
# Install into a temporary directory. The temporary directory does not
# need to be removed as bazel tests are run in a scratch space.
tmp_folder = "tmp"
result = install_test_helper.install(tmp_folder, ['lib', 'share'])
self.assertEqual(None, result)
# Override PYTHONPATH to only use the installed `pydrake` module.
env_python_path = "PYTHONPATH"
tool_env = dict(os.environ)
tool_env[env_python_path] = os.path.abspath(
os.path.join(tmp_folder, "lib", "python2.7", "site-packages")
os.path.join(install_test_helper.get_install_dir(),
"lib", "python2.7", "site-packages")
)
data_folder = os.path.join(tmp_folder, "share", "drake")
# Call the same Python binary. On Mac, calling the system `python`
# would result in a crash since pydrake was built against brew python.
# On Linux, this will still work and force using
# python2 which should be a link to the actual executable.
data_folder = os.path.join(install_test_helper.get_install_dir(),
"share", "drake")
# Calling `pydrake.getDrakePath()` twice verifies that there
# is no memory allocation issue in the C code.
output_path = subprocess.check_output(
[sys.executable,
output_path = install_test_helper.check_output(
[install_test_helper.get_python_executable(),
"-c", "import pydrake; print(pydrake.getDrakePath());\
import pydrake; print(pydrake.getDrakePath())"
],
cwd='/', # Defeat the "search in parent folders" heuristic.
env=tool_env,
).strip()
found_install_path = (data_folder in output_path)
self.assertEqual(found_install_path, True)
self.assertTrue(found_install_path)


if __name__ == '__main__':
Expand Down
13 changes: 6 additions & 7 deletions common/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ drake_py_binary(

install(
name = "install",
install_tests = [":test/resource_tool_installed_test.py"],
targets = [":resource_tool"],
# TODO(jwnimmer-tri) The install rule should offer more specific options
# for programs not in bin/, and including the workspace and/or package name
Expand Down Expand Up @@ -989,17 +990,15 @@ drake_py_test(
],
)

drake_py_test(
name = "resource_tool_installed_test",
srcs = ["test/resource_tool_installed_test.py"],
deps = ["//tools/install:install_test_helper"],
)

# TODO(jwnimmer-tri) These tests are currently missing...
# - drake_assert_test in fancy variants
# - drake_assert_test_compile in fancy variants
# - text_logging_test in fancy variants
# - drake_deprecated_test in fancy variants
# - cpplint_wrapper_test.py

add_lint_tests()
add_lint_tests(
python_lint_extra_srcs = [
":test/resource_tool_installed_test.py",
],
)
44 changes: 26 additions & 18 deletions common/test/resource_tool_installed_test.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
#!/usr/bin/env python

"""Performs tests for resource_tool as used _after_ installation.
"""

import os
import subprocess
import unittest
import install_test_helper


class TestResourceTool(unittest.TestCase):
def test_install_and_run(self):
# Install into a temporary directory.
result = install_test_helper.install("tmp", ["libexec"])
self.assertEqual(None, result)

install_dir = install_test_helper.get_install_dir()
# Create a resource in the temporary directory.
os.makedirs("tmp/share/drake/common/test")
with open("tmp/share/drake/common/test/tmp_resource", "w") as f:
f.write("tmp_resource")
tmp_dir = install_test_helper.create_temporary_dir()

# Verify un-installed copy was removed, so we _know_ it won't be used.
self.assertEqual(os.listdir(os.getcwd()), ["tmp"])
resource_folder = os.path.join(tmp_dir, "share/drake/")
test_folder = os.path.join(resource_folder, "common/test")
os.makedirs(test_folder)
# Create sentinel file.
sentinel = os.path.join(resource_folder,
".drake-find_resource-sentinel")
with open(sentinel, "w") as f:
f.write("")
# Create resource file.
resource = os.path.join(test_folder, "tmp_resource")
resource_data = "tmp_resource"
with open(resource, "w") as f:
f.write(resource_data)

# Cross-check the resource root environment variable name.
env_name = "DRAKE_RESOURCE_ROOT"
resource_tool = "tmp/share/drake/common/resource_tool"
output_name = subprocess.check_output(
resource_tool = os.path.join(
install_dir, "share/drake/common/resource_tool")
output_name = install_test_helper.check_output(
[resource_tool,
"--print_resource_root_environment_variable_name",
],
Expand All @@ -33,28 +41,28 @@ def test_install_and_run(self):

# Use the installed resource_tool to find a resource.
tool_env = dict(os.environ)
tool_env[env_name] = "tmp/share"
absolute_path = subprocess.check_output(
tool_env[env_name] = os.path.join(tmp_dir, "share")
absolute_path = install_test_helper.check_output(
[resource_tool,
"--print_resource_path",
"drake/common/test/tmp_resource",
],
env=tool_env,
).strip()
with open(absolute_path, 'r') as data:
self.assertEqual(data.read(), "tmp_resource")
self.assertEqual(data.read(), resource_data)

# Remove environment variable.
absolute_path = subprocess.check_output(
absolute_path = install_test_helper.check_output(
[resource_tool,
"--print_resource_path",
"drake/common/test/tmp_resource",
"--add_resource_search_path",
"tmp/share",
os.path.join(tmp_dir, "share"),
],
).strip()
with open(absolute_path, 'r') as data:
self.assertEqual(data.read(), "tmp_resource")
self.assertEqual(data.read(), resource_data)


if __name__ == '__main__':
Expand Down
39 changes: 10 additions & 29 deletions examples/kuka_iiwa_arm/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -244,24 +244,14 @@ sh_test(
tags = ["no_kcov"],
)

# `//:install` is run in this test to verify that once installed
# these examples still work. This test fails when bazel is run with
# `no_everything` because `libgurobi70.so` is not found [Issue #7283].
drake_py_test(
name = "install_test",
size = "medium",
# Increase the timeout so that debug builds are successful.
timeout = "long",
srcs = ["test/install_test.py"],
data = ["//:install"],
main = "test/install_test.py",
tags = ["no_everything"],
deps = ["//tools/install:install_test_helper"],
)

# This examples needs to be install for external projects such as Spartan.
install(
name = "install",
install_tests = [
":test/iiwa_wsg_simulation_installed_test.py",
":test/kuka_plan_runner_installed_test.py",
":test/kuka_simulation_installed_test.py",
],
targets = [
":iiwa_wsg_simulation",
":kuka_plan_runner",
Expand Down Expand Up @@ -291,19 +281,10 @@ install(
],
)

# Run the installed flavor of the kuka simulation, to make sure it can locate
# its resources, etc. TODO(jwnimmer-tri) Refactor installed-test phrasing to
# be more compact, convenient, and efficient such as #7774 proposes.
drake_py_test(
name = "kuka_simulation_installed_test",
size = "small",
timeout = "long",
srcs = ["test/kuka_simulation_installed_test.py"],
data = ["//:install"],
main = "test/kuka_simulation_installed_test.py",
deps = [
"//tools/install:install_test_helper",
add_lint_tests(
python_lint_extra_srcs = [
":test/iiwa_wsg_simulation_installed_test.py",
":test/kuka_plan_runner_installed_test.py",
":test/kuka_simulation_installed_test.py",
],
)

add_lint_tests()
25 changes: 25 additions & 0 deletions examples/kuka_iiwa_arm/test/iiwa_wsg_simulation_installed_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python

import os
import shutil
import unittest
import sys
import install_test_helper


class TestIiwaWsgSimulation(unittest.TestCase):
def test_install(self):
# Get install directory.
install_dir = install_test_helper.get_install_dir()
# Make sure the simulation can run without error. We set cwd="/" to
# defeat the "search in parent folders" heuristic, so that the "use
# libdrake.so relative paths" must be successful.
simulation = os.path.join(
install_dir,
"share/drake/examples/kuka_iiwa_arm/iiwa_wsg_simulation")
self.assertTrue(os.path.exists(simulation), "Can't find " + simulation)
install_test_helper.check_call([simulation, "--simulation_sec=0.01"])


if __name__ == '__main__':
unittest.main()
14 changes: 14 additions & 0 deletions examples/kuka_iiwa_arm/test/kuka_plan_runner_installed_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env python

import unittest
import install_test_helper


class TestKukaSimulation(unittest.TestCase):
def test_install(self):
install_test_helper.run_and_kill(
['share/drake/examples/kuka_iiwa_arm/kuka_plan_runner'])


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

0 comments on commit a7ba439

Please sign in to comment.