Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[smart_holder] Add test_namespace_visibility #4050

Merged
merged 11 commits into from
Jul 13, 2022
Merged
13 changes: 11 additions & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ set(PYBIND11_TEST_FILES
test_eigen
test_enum
test_eval
test_exc_namespace_visibility.py
test_exceptions
test_factory_constructors
test_gil_scoped
Expand Down Expand Up @@ -236,6 +237,8 @@ tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
tests_extra_targets("test_class_sh_module_local.py"
"class_sh_module_local_0;class_sh_module_local_1;class_sh_module_local_2")
tests_extra_targets("test_exc_namespace_visibility.py"
"namespace_visibility_1;namespace_visibility_2")

set(PYBIND11_EIGEN_REPO
"https://gitlab.com/libeigen/eigen.git"
Expand Down Expand Up @@ -426,8 +429,14 @@ if(PYBIND11_CUDA_TESTS)
endif()

foreach(target ${test_targets})
set(test_files ${PYBIND11_TEST_FILES})
if(NOT "${target}" STREQUAL "pybind11_tests")
if("${target}" STREQUAL "pybind11_tests")
set(test_files ${PYBIND11_TEST_FILES})
elseif("${target}" STREQUAL "namespace_visibility_1")
set(test_files namespace_visibility_1s.cpp)
if(PYBIND11_CUDA_TESTS)
set_property(SOURCE namespace_visibility_1s.cpp PROPERTY LANGUAGE CUDA)
endif()
else()
set(test_files "")
endif()

Expand Down
31 changes: 31 additions & 0 deletions tests/namespace_visibility.inl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2022 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#pragma once

#include <cstddef>

#ifdef __GNUG__
# define PYBIND11_NS_VIS_U /* unspecified */
# define PYBIND11_NS_VIS_H __attribute__((visibility("hidden")))
#else
# define PYBIND11_NS_VIS_U
# define PYBIND11_NS_VIS_H
#endif

#define PYBIND11_NS_VIS_FUNC \
inline std::ptrdiff_t func() { \
static std::ptrdiff_t value = 0; \
return reinterpret_cast<std::ptrdiff_t>(&value); \
}

#define PYBIND11_NS_VIS_DEFS \
m.def("ns_vis_uuu_func", pybind11_ns_vis_uuu::func); \
m.def("ns_vis_uuh_func", pybind11_ns_vis_uuh::func); \
m.def("ns_vis_uhu_func", pybind11_ns_vis_uhu::func); \
m.def("ns_vis_uhh_func", pybind11_ns_vis_uhh::func); \
m.def("ns_vis_huu_func", pybind11_ns_vis_huu::func); \
m.def("ns_vis_huh_func", pybind11_ns_vis_huh::func); \
m.def("ns_vis_hhu_func", pybind11_ns_vis_hhu::func); \
m.def("ns_vis_hhh_func", pybind11_ns_vis_hhh::func);
24 changes: 24 additions & 0 deletions tests/namespace_visibility_1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "pybind11/pybind11.h"
#include "namespace_visibility.inl"

// clang-format off
namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
// ^ ^
// bit used .............. here
// clang-format on

void namespace_visibility_1s(pybind11::module_ &m);

PYBIND11_MODULE(namespace_visibility_1, m) {
PYBIND11_NS_VIS_DEFS

auto sm = m.def_submodule("submodule");
namespace_visibility_1s(sm);
}
17 changes: 17 additions & 0 deletions tests/namespace_visibility_1s.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "pybind11/pybind11.h"
#include "namespace_visibility.inl"

// clang-format off
namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
// ^ ^
// bit used ............. here
// clang-format on

void namespace_visibility_1s(pybind11::module_ &m) { PYBIND11_NS_VIS_DEFS }
17 changes: 17 additions & 0 deletions tests/namespace_visibility_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "pybind11/pybind11.h"
#include "namespace_visibility.inl"

// clang-format off
namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
// ^ ^
// bit used ............ here
// clang-format on

PYBIND11_MODULE(namespace_visibility_2, m) { PYBIND11_NS_VIS_DEFS }
67 changes: 67 additions & 0 deletions tests/test_exc_namespace_visibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# This is not really a unit test, but probing environment/toolchain/platform-specific
# behavior under the exact same conditions as normal tests.
# The results are useful to understanding the effects of, e.g., removing
# `-fvisibility=hidden` or `__attribute__((visibility("hidden")))`, or linking
# extensions statically with the core Python interpreter.

# NOTE
# ====
# The "exc_" in "test_exc_namespace_visibility.py" is a workaround, to avoid a
# test_cross_module_exception_translator (test_exceptions.py) failure. This
# test has to be imported (by pytest) before test_exceptions.py; pytest sorts
# lexically. See https://github.com/pybind/pybind11/pull/4054 for more information.

import itertools

import namespace_visibility_1
import namespace_visibility_2
import pytest

# Please take a quick look at namespace_visibility.h first, to see what is being probed.
#
# EXPECTED is for -fvisibility=hidden or equivalent, as recommended in the docs.
EXPECTED_ALL_UNIQUE_POINTERS_OBSERVED = "AAC:AAc:AaC:Aac:aAC:aAc:aaC:aac"
rwgk marked this conversation as resolved.
Show resolved Hide resolved
# ^^^
# namespace_visibility_1 pointer|||
# namespace_visibility_1.submodule pointer|| (identical letters means same pointer)
# namespace_visibility_2 pointer|
# Upper-case: namespace visibility unspecified
# Lower-case: namespace visibility hidden
# This test probes all 2**3 combinations of u/h ** number-of-sub/modules.
#
# Also observed:
# AAA:AAc:AaC:Aac:aAC:aAc:aaC:aac -fvisibility=default Linux
# AAA:AAc:AaA:Aac:aAC:aAc:aaC:aac -fvisibility=default macOS
# AAA:AAa:AaA:Aaa:aAA:aAa:aaA:aaa everything linked statically


def test_namespace_visibility():
modules = (
namespace_visibility_1,
namespace_visibility_1.submodule,
namespace_visibility_2,
)
unique_pointer_labels = "ABC"
unique_pointers_observed = []
# u = visibility unspecified
# h = visibility hidden
for visibility in itertools.product(*([("u", "h")] * len(modules))):
# See functions in namespace_visibility_*.cpp
func = "ns_vis_" + "".join(visibility) + "_func"
ptrs = []
uq_ptrs_obs = ""
for vis, m in zip(visibility, modules):
ptr = getattr(m, func)()
ptrs.append(ptr)
lbl = unique_pointer_labels[ptrs.index(ptr)]
if vis == "h":
# Encode u/h info as upper/lower case to make the final result
# as compact as possible.
lbl = lbl.lower()
uq_ptrs_obs += lbl
unique_pointers_observed.append(uq_ptrs_obs)
all_unique_pointers_observed = ":".join(unique_pointers_observed)
if all_unique_pointers_observed != EXPECTED_ALL_UNIQUE_POINTERS_OBSERVED:
pytest.skip(
f"UNUSUAL all_unique_pointers_observed: {all_unique_pointers_observed}"
)