Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@ GTEST_SRCS := $(wildcard $(GTEST_DIR)/src/*.cc) \
$(wildcard $(GTEST_DIR)/src/*.h) $(GTEST_HEADERS)
GTEST_FILTER ?= '*'

TEST_SOURCES := $(wildcard tests/*.cc)
TEST_SOURCES := $(wildcard tests/*.cc) $(wildcard tests/library_wrappers/*.cc)
TEST_DFILES := $(TEST_SOURCES:.cc=.d)
TEST_OBJECTS := $(TEST_SOURCES:.cc=.o)
TEST_HEADERS := $(wildcard tests/*.h) $(GTEST_HEADERS)
TEST_HEADERS := $(wildcard tests/*.h) $(wildcard tests/library_wrappers/*.h) $(GTEST_HEADERS)
TEST_INCLUDE := -I tests -I $(GTEST_DIR)/include
TEST_MODULE := tests/_runner$(SO_SUFFIX)
PYTHON_TESTS := $(wildcard tests/*.py)
Expand Down
20 changes: 4 additions & 16 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,13 @@ libpy requires:
- gcc>=8 or clang>=10
- numpy>=1.11.3

libpy also depends on the following system packages:
Optional Requirements
---------------------

- google sparsehash

To install these dependencies:

ubuntu
~~~~~~

.. code-block:: bash
libpy optionally provides wrappers for the following libraries:

$ sudo apt install libsparsehash-dev

macOS
~~~~~

.. code-block:: bash
- google sparsehash

$ brew install google-sparsehash

Install
-------
Expand Down
21 changes: 4 additions & 17 deletions docs/source/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,12 @@ lipy requires:
- gcc>=8 or clang>=10
- numpy>=1.11.3

libpy also depends on the following system packages:
Optional Requirements
---------------------

- google sparsehash

To install these dependencies:

ubuntu
~~~~~~

.. code-block:: bash
libpy optionally provides wrappers for the following libraries:

$ sudo apt install libsparsehash-dev

macOS
~~~~~

.. code-block:: bash

$ brew install google-sparsehash
- google sparsehash

Install
-------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <sparsehash/dense_hash_map>
#include <sparsehash/dense_hash_set>
#include <sparsehash/sparse_hash_map>
#include <sparsehash/sparse_hash_set>

#include "libpy/to_object.h"

Expand Down Expand Up @@ -153,5 +154,21 @@ template<typename Key, typename HashFcn, typename EqualKey, typename Alloc>
struct to_object<dense_hash_set<Key, HashFcn, EqualKey, Alloc>>
: public set_to_object<dense_hash_set<Key, HashFcn, EqualKey, Alloc>> {};

template<typename Key, typename T, typename HashFcn, typename EqualKey, typename Alloc>
struct to_object<google::dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>>
: public map_to_object<google::dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>> {};

template<typename Key, typename T, typename HashFcn, typename EqualKey, typename Alloc>
struct to_object<google::sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>>
: public map_to_object<google::sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>> {};

template<typename Key, typename HashFcn, typename EqualKey, typename Alloc>
struct to_object<google::dense_hash_set<Key, HashFcn, EqualKey, Alloc>>
: public set_to_object<google::dense_hash_set<Key, HashFcn, EqualKey, Alloc>> {};

template<typename Key, typename HashFcn, typename EqualKey, typename Alloc>
struct to_object<google::sparse_hash_set<Key, HashFcn, EqualKey, Alloc>>
: public set_to_object<google::sparse_hash_set<Key, HashFcn, EqualKey, Alloc>> {};

} // namespace dispatch
} // namespace py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,44 @@
#include "gtest/gtest.h"

#include "libpy/datetime64.h"
#include "libpy/dense_hash_map.h"
#include "libpy/itertools.h"
#include "libpy/library_wrappers/sparsehash.h"

#include "test_utils.h"

namespace test_sparsehash {

using namespace std::literals;
using namespace py::cs::literals;

class sparsehash_to_object : public with_python_interpreter {};

TEST_F(sparsehash_to_object, sparse_hash_map) {
auto map = google::sparse_hash_map<std::string, bool>();
py_test::test_map_to_object_impl(map);
}

TEST_F(sparsehash_to_object, dense_hash_map) {
auto map = google::dense_hash_map<std::string, bool>();
map.set_empty_key("the_empty_key"s);

py_test::test_map_to_object_impl(map);
}

TEST_F(sparsehash_to_object, sparse_hash_set) {
auto filler = py_test::examples<std::string>();
auto a = google::sparse_hash_set<std::string>(filler.begin(), filler.end());
py_test::test_set_to_object_impl(a);
}

TEST_F(sparsehash_to_object, dense_hash_set) {
auto filler = py_test::examples<std::string>();
auto a = google::dense_hash_set<std::string>(filler.begin(),
filler.end(),
"the_empty_key"s);
py_test::test_set_to_object_impl(a);
}

namespace test_dense_hash_map {
TEST(dense_hash_map, invalid_empty_key) {
using double_key = py::dense_hash_map<double, int>;
EXPECT_THROW((double_key{std::numeric_limits<double>::quiet_NaN()}),
Expand Down Expand Up @@ -42,4 +77,5 @@ TEST(dense_hash_set, invalid_empty_key) {
EXPECT_THROW((M8_key{py::datetime64ns::nat()}), std::invalid_argument);
EXPECT_THROW((M8_key{py::datetime64ns::nat(), 10}), std::invalid_argument);
}
} // namespace test_dense_hash_map

} // namespace test_sparsehash
3 changes: 1 addition & 2 deletions tests/test_any.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "gtest/gtest.h"

#include "libpy/any.h"
#include "libpy/dense_hash_map.h"

namespace test_any {
TEST(any_vtable, void_vtable) {
Expand Down Expand Up @@ -88,7 +87,7 @@ TEST(any_vtable, ostream_format) {
}

TEST(any_vtable, map_key) {
py::dense_hash_map<py::any_vtable, int> map(py::any_vtable{});
std::unordered_map<py::any_vtable, int> map;

map[py::any_vtable::make<int>()] = 0;
map[py::any_vtable::make<float>()] = 1;
Expand Down
3 changes: 0 additions & 3 deletions tests/test_object_map_key.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

#include "gtest/gtest.h"

#include "libpy/dense_hash_map.h"
#include "libpy/exception.h"
#include "libpy/meta.h"
#include "libpy/object_map_key.h"
Expand Down Expand Up @@ -241,8 +240,6 @@ void test_use_in_map(M map) {
TEST_F(object_map_key, use_in_map) {
test_use_in_map(std::map<py::object_map_key, int>{});
test_use_in_map(std::unordered_map<py::object_map_key, int>{});
test_use_in_map(py::sparse_hash_map<py::object_map_key, int>{});
test_use_in_map(py::dense_hash_map<py::object_map_key, int>{py::object_map_key{}});
}

TEST_F(object_map_key, convert) {
Expand Down
157 changes: 10 additions & 147 deletions tests/test_to_object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

#include "libpy/any.h"
#include "libpy/char_sequence.h"
#include "libpy/dense_hash_map.h"
#include "libpy/itertools.h"
#include "libpy/meta.h"
#include "libpy/numpy_utils.h"
Expand All @@ -21,162 +20,26 @@ using namespace py::cs::literals;

class to_object : public with_python_interpreter {};

template<typename T>
std::array<T, 3> examples();

template<>
std::array<std::int64_t, 3> examples() {
return {-200, 0, 1000};
}

template<>
std::array<std::string, 3> examples() {
return {"foo", "", "arglebargle"};
}

template<>
std::array<std::array<char, 3>, 3> examples() {
std::array<char, 3> foo{'f', 'o', 'o'};
std::array<char, 3> bar{'b', 'a', 'r'};
std::array<char, 3> baz{'b', 'a', 'z'};
return {foo, bar, baz};
}

template<>
std::array<bool, 3> examples() {
return {true, false, true};
}

template<>
std::array<double, 3> examples() {
return {-1.0, -0.0, 100.0};
}

template<>
std::array<py::owned_ref<>, 3> examples() {
Py_INCREF(Py_True);
Py_INCREF(Py_False);
Py_INCREF(Py_None);
return {py::owned_ref<>(Py_True),
py::owned_ref<>(Py_False),
py::owned_ref<>(Py_None)};
}

template<typename M>
void test_map_to_object_impl(M m) {

// Fill the map with some example values.
auto it = py::zip(examples<typename M::key_type>(),
examples<typename M::mapped_type>());
for (auto [key, value] : it) {
m[key] = value;
}

auto check_python_map = [&](py::owned_ref<PyObject> ob) {
ASSERT_TRUE(ob) << "to_object should not return null";
EXPECT_TRUE(PyDict_Check(ob.get()));

// Python map should be the same length as C++ map.
Py_ssize_t len = PyDict_Size(ob.get());
EXPECT_EQ(std::size_t(len), m.size())
<< "Python dict length should match C++ map length.";

// Key/Value pairs in the python map should match the result of calling
// to_object on each key/value pair in the C++ map.
for (auto& [cxx_key, cxx_value] : m) {
auto py_key = py::to_object(cxx_key);
auto py_value = py::to_object(cxx_value);

py::borrowed_ref result = PyDict_GetItem(ob.get(), py_key.get());
ASSERT_TRUE(result) << "Key should have been in the map";

bool values_equal =
PyObject_RichCompareBool(py_value.get(), result.get(), Py_EQ);
EXPECT_EQ(values_equal, 1) << "Dict values were not equal";
}
};

// Check to_object with value, const value, and rvalue reference.

py::owned_ref<PyObject> result = py::to_object(m);
check_python_map(result);

const M& const_ref = m;
py::owned_ref<PyObject> constref_result = py::to_object(const_ref);
check_python_map(constref_result);

M copy = m; // Make a copy before moving b/c the lambda above uses ``m``.
py::owned_ref<PyObject> rvalueref_result = py::to_object(std::move(copy));
check_python_map(rvalueref_result);
}

TEST_F(to_object, map_to_object) {
// NOTE: This test takes a long time to compile (about a .5s per entry in this
// tuple). This is just enough coverage to test all three of our hash table types,
// and a few important key/value types.
auto maps = std::make_tuple(py::dense_hash_map<std::string, py::owned_ref<PyObject>>(
"missing_value"s),
py::sparse_hash_map<std::int64_t, std::array<char, 3>>(),
std::unordered_map<std::string, bool>());

// Call test_map_to_object_impl on each entry in ``maps``.
std::apply([&](auto... map) { (test_map_to_object_impl(map), ...); }, maps);
}

template<typename V>
void test_sequence_to_object_impl(V v) {
auto check_python_list = [&](py::owned_ref<PyObject> ob) {
ASSERT_TRUE(ob) << "to_object should not return null";
EXPECT_EQ(PyList_Check(ob.get()), 1) << "ob should be a list";

Py_ssize_t len = PyList_GET_SIZE(ob.get());
EXPECT_EQ(std::size_t(len), v.size())
<< "Python list length should match C++ vector length.";

// Values in Python list should be the result of calling to_object on each entry
// in the C++ vector.
for (auto [i, cxx_value] : py::enumerate(v)) {
auto py_value = py::to_object(cxx_value);

py::borrowed_ref result = PyList_GetItem(ob.get(), i);
ASSERT_TRUE(result) << "Should have had a value at index " << i;

bool values_equal =
PyObject_RichCompareBool(py_value.get(), result.get(), Py_EQ);
EXPECT_EQ(values_equal, 1)
<< "List values at index " << i << " were not equal";
}
};

// Check to_object with value, const value, and rvalue reference.

py::owned_ref<PyObject> result = py::to_object(v);
check_python_list(result);

const V& const_ref = v;
py::owned_ref<PyObject> constref_result = py::to_object(const_ref);
check_python_list(constref_result);

V copy = v; // Make a copy before moving b/c the lambda above uses ``v``.
py::owned_ref<PyObject> rvalueref_result = py::to_object(std::move(copy));
check_python_list(rvalueref_result);
auto map = std::unordered_map<std::string, bool>();
py_test::test_map_to_object_impl(map);
}

TEST_F(to_object, vector_to_object) {
auto to_vec = [](const auto& arr) { return std::vector(arr.begin(), arr.end()); };
auto vectors = std::make_tuple(to_vec(examples<std::string>()),
to_vec(examples<double>()),
to_vec(examples<py::owned_ref<>>()));
auto vectors = std::make_tuple(to_vec(py_test::examples<std::string>()),
to_vec(py_test::examples<double>()),
to_vec(py_test::examples<py::owned_ref<>>()));
// Call test_sequence_to_object_impl on each entry in `vectors`.
std::apply([&](auto... vec) { (test_sequence_to_object_impl(vec), ...); }, vectors);
std::apply([&](auto... vec) { (py_test::test_sequence_to_object_impl(vec), ...); }, vectors);
}

TEST_F(to_object, array_to_object) {
auto arrays = std::make_tuple(examples<std::string>(),
examples<double>(),
examples<py::owned_ref<>>());
auto arrays = std::make_tuple(py_test::examples<std::string>(),
py_test::examples<double>(),
py_test::examples<py::owned_ref<>>());
// Call test_sequence_to_object_impl on each entry in `arrays`.
std::apply([&](auto... arr) { (test_sequence_to_object_impl(arr), ...); }, arrays);
std::apply([&](auto... arr) { (py_test::test_sequence_to_object_impl(arr), ...); }, arrays);
}

template<typename R, typename T>
Expand Down
Loading