Skip to content

Commit

Permalink
add .iterate_enum_variants() and clean up testing in test_ctypes_inte…
Browse files Browse the repository at this point in the history
…gration.py

(this makes addressing pantsbuild#6866 later a little easier)
  • Loading branch information
cosmicexplorer committed Feb 12, 2019
1 parent cc26f22 commit f19ff49
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 49 deletions.
12 changes: 12 additions & 0 deletions src/python/pants/util/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class TypedDatatypeInstanceConstructionError(TypeCheckError):
"""Raised when a datatype()'s fields fail a type check upon construction."""


# TODO: create a mixin which declares/implements the methods we define on the generated class in
# datatype() and enum() to decouple the class's logic from the way it's created. This may also make
# migration to python 3 dataclasses as per #7074 easier.
def datatype(field_decls, superclass_name=None, **kwargs):
"""A wrapper for `namedtuple` that accounts for the type of the object in equality.
Expand Down Expand Up @@ -319,6 +322,15 @@ def resolve_for_enum_variant(self, mapping):
match_for_variant = mapping[self._get_value(self)]
return match_for_variant

@classmethod
def iterate_enum_variants(cls):
"""Iterate over all instances of this enum, in the declared order.
NB: This method is exposed for testing enum variants easily. resolve_for_enum_variant() should
be used for performing conditional logic based on an enum instance's value.
"""
return [cls.create(variant_value) for variant_value in cls.allowed_values]

return ChoiceDatatype


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def test_target_level_toolchain_variant_llvm(self):

task = self.create_task(self.context(target_roots=[cpp_lib_target]))
compiler = task.get_compiler(cpp_lib_target)
# TODO(#6866): test specifically which compiler is selected, traversing the PATH if necessary.
self.assertIn('llvm', compiler.path_entries[0])

def test_target_level_toolchain_variant_default_llvm(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import glob
import os
import re
from functools import wraps
from zipfile import ZipFile

from pants.backend.native.config.environment import Platform
Expand All @@ -24,6 +25,14 @@ def invoke_pex_for_output(pex_file_to_run):
return subprocess.check_output([pex_file_to_run], stderr=subprocess.STDOUT)


def _toolchain_variants(func):
@wraps(func)
def wrapper(*args, **kwargs):
for variant in ToolchainVariant.iterate_enum_variants():
func(*args, toolchain_variant=variant, **kwargs)
return wrapper


class CTypesIntegrationTest(PantsRunIntegrationTest):

_binary_target_dir = 'testprojects/src/python/python_distribution/ctypes'
Expand All @@ -38,45 +47,40 @@ class CTypesIntegrationTest(PantsRunIntegrationTest):
'testprojects/src/python/python_distribution/ctypes_with_extra_compiler_flags:bin'
)

def test_ctypes_binary_creation(self):
@_toolchain_variants
def test_ctypes_binary_creation(self, toolchain_variant):
"""Create a python_binary() with all native toolchain variants, and test the result."""
# TODO: this pattern could be made more ergonomic for `enum()`, along with exhaustiveness
# checking.
for variant in ToolchainVariant.allowed_values:
self._assert_ctypes_binary_creation(variant)

_compiler_names_for_variant = {
'gnu': ['gcc', 'g++'],
'llvm': ['clang', 'clang++'],
}

# All of our toolchains currently use the C++ compiler's filename as argv[0] for the linker.
_linker_names_for_variant = {
'gnu': ['g++'],
'llvm': ['clang++'],
}

def _assert_ctypes_binary_creation(self, toolchain_variant):
with temporary_dir() as tmp_dir:
pants_run = self.run_pants(command=['binary', self._binary_target], config={
GLOBAL_SCOPE_CONFIG_SECTION: {
'pants_distdir': tmp_dir,
},
'native-build-step': {
'toolchain_variant': toolchain_variant,
'toolchain_variant': toolchain_variant.value,
},
})

self.assert_success(pants_run)

# Check that we have selected the appropriate compilers for our selected toolchain variant,
# for both C and C++ compilation.
# TODO(#6866): don't parse info logs for testing!
for compiler_name in self._compiler_names_for_variant[toolchain_variant]:
# TODO(#6866): don't parse info logs for testing! There is a TODO in test_cpp_compile.py
# in the native backend testing to traverse the PATH to find the selected compiler.
compiler_names_to_check = toolchain_variant.resolve_for_enum_variant({
'gnu': ['gcc', 'g++'],
'llvm': ['clang', 'clang++'],
})
for compiler_name in compiler_names_to_check:
self.assertIn("selected compiler exe name: '{}'".format(compiler_name),
pants_run.stdout_data)

for linker_name in self._linker_names_for_variant[toolchain_variant]:
# All of our toolchains currently use the C++ compiler's filename as argv[0] for the linker,
# so there is only one name to check.
linker_names_to_check = toolchain_variant.resolve_for_enum_variant({
'gnu': ['g++'],
'llvm': ['clang++'],
})
for linker_name in linker_names_to_check:
self.assertIn("selected linker exe name: '{}'".format(linker_name),
pants_run.stdout_data)

Expand Down Expand Up @@ -110,16 +114,8 @@ def _assert_ctypes_binary_creation(self, toolchain_variant):
binary_run_output = invoke_pex_for_output(pex)
self.assertEqual(b'x=3, f(x)=17\n', binary_run_output)

def test_ctypes_native_language_interop(self):
for variant in ToolchainVariant.allowed_values:
self._assert_ctypes_interop_with_mock_buildroot(variant)

_include_not_found_message_for_variant = {
'gnu': "fatal error: some_math.h: No such file or directory",
'llvm': "fatal error: 'some_math.h' file not found"
}

def _assert_ctypes_interop_with_mock_buildroot(self, toolchain_variant):
@_toolchain_variants
def test_ctypes_native_language_interop(self, toolchain_variant):
# TODO: consider making this mock_buildroot/run_pants_with_workdir into a
# PantsRunIntegrationTest method!
with self.mock_buildroot(
Expand All @@ -138,7 +134,7 @@ def _assert_ctypes_interop_with_mock_buildroot(self, toolchain_variant):
# Explicitly set to True (although this is the default).
config={
'native-build-step': {
'toolchain_variant': toolchain_variant,
'toolchain_variant': toolchain_variant.value,
},
# TODO(#6848): don't make it possible to forget to add the toolchain_variant option!
'native-build-settings': {
Expand All @@ -148,19 +144,25 @@ def _assert_ctypes_interop_with_mock_buildroot(self, toolchain_variant):
workdir=os.path.join(buildroot.new_buildroot, '.pants.d'),
build_root=buildroot.new_buildroot)
self.assert_failure(pants_binary_strict_deps_failure)
self.assertIn(self._include_not_found_message_for_variant[toolchain_variant],
self.assertIn(toolchain_variant.resolve_for_enum_variant({
'gnu': "fatal error: some_math.h: No such file or directory",
'llvm': "fatal error: 'some_math.h' file not found",
}),
pants_binary_strict_deps_failure.stdout_data)

# TODO(#6848): we need to provide the libstdc++.so.6.dylib which comes with gcc on osx in the
# DYLD_LIBRARY_PATH during the 'run' goal somehow.
attempt_pants_run = Platform.create().resolve_for_enum_variant({
'darwin': toolchain_variant != 'gnu',
'darwin': toolchain_variant.resolve_for_enum_variant({
'gnu': False,
'llvm': True,
}),
'linux': True,
})
if attempt_pants_run:
pants_run_interop = self.run_pants(['-q', 'run', self._binary_target_with_interop], config={
'native-build-step': {
'toolchain_variant': toolchain_variant,
'toolchain_variant': toolchain_variant.value,
},
'native-build-settings': {
'strict_deps': True,
Expand All @@ -169,14 +171,11 @@ def _assert_ctypes_interop_with_mock_buildroot(self, toolchain_variant):
self.assert_success(pants_run_interop)
self.assertEqual('x=3, f(x)=299\n', pants_run_interop.stdout_data)

def test_ctypes_third_party_integration(self):
for variant in ToolchainVariant.allowed_values:
self._assert_ctypes_third_party_integration(variant)

def _assert_ctypes_third_party_integration(self, toolchain_variant):
@_toolchain_variants
def test_ctypes_third_party_integration(self, toolchain_variant):
pants_binary = self.run_pants(['binary', self._binary_target_with_third_party], config={
'native-build-step': {
'toolchain_variant': toolchain_variant,
'toolchain_variant': toolchain_variant.value,
},
})
self.assert_success(pants_binary)
Expand All @@ -190,7 +189,7 @@ def _assert_ctypes_third_party_integration(self, toolchain_variant):
if attempt_pants_run:
pants_run = self.run_pants(['-q', 'run', self._binary_target_with_third_party], config={
'native-build-step': {
'toolchain_variant': toolchain_variant,
'toolchain_variant': toolchain_variant.value,
},
})
self.assert_success(pants_run)
Expand Down Expand Up @@ -220,23 +219,20 @@ def test_pants_native_source_detection_for_local_ctypes_dists_for_current_platfo
self.assert_success(pants_run)
self.assertIn('x=3, f(x)=17', pants_run.stdout_data)

def test_native_compiler_option_sets_integration(self):
@_toolchain_variants
def test_native_compiler_option_sets_integration(self, toolchain_variant):
"""Test that native compilation includes extra compiler flags from target definitions.
This target uses the ndebug and asdf option sets.
If either of these are not present (disabled), this test will fail.
"""
for variant in ToolchainVariant.allowed_values:
self._assert_ctypes_third_party_integration(variant)

def _assert_native_compiler_option_sets_integration(self, toolchain_variant):
command = [
'run',
self._binary_target_with_compiler_option_sets
]
pants_run = self.run_pants(command=command, config={
'native-build-step': {
'toolchain_variant': toolchain_variant,
'toolchain_variant': toolchain_variant.value,
},
'native-build-step.cpp-compile-settings': {
'compiler_option_sets_enabled_args': {
Expand Down

0 comments on commit f19ff49

Please sign in to comment.