Commit 0bc272b2 by Dean Moldovan

Move tests from short translation units into their logical parents

parent 83e328f5
...@@ -325,7 +325,7 @@ ensuring member initialization and (eventual) destruction. ...@@ -325,7 +325,7 @@ ensuring member initialization and (eventual) destruction.
.. seealso:: .. seealso::
See the file :file:`tests/test_alias_initialization.cpp` for complete examples See the file :file:`tests/test_virtual_functions.cpp` for complete examples
showing both normal and forced trampoline instantiation. showing both normal and forced trampoline instantiation.
.. _custom_constructors: .. _custom_constructors:
......
...@@ -26,7 +26,6 @@ endif() ...@@ -26,7 +26,6 @@ endif()
# Full set of test files (you can override these; see below) # Full set of test files (you can override these; see below)
set(PYBIND11_TEST_FILES set(PYBIND11_TEST_FILES
test_alias_initialization.cpp
test_buffers.cpp test_buffers.cpp
test_builtin_casters.cpp test_builtin_casters.cpp
test_call_policies.cpp test_call_policies.cpp
...@@ -40,7 +39,6 @@ set(PYBIND11_TEST_FILES ...@@ -40,7 +39,6 @@ set(PYBIND11_TEST_FILES
test_enum.cpp test_enum.cpp
test_eval.cpp test_eval.cpp
test_exceptions.cpp test_exceptions.cpp
test_inheritance.cpp
test_kwargs_and_defaults.cpp test_kwargs_and_defaults.cpp
test_methods_and_attributes.cpp test_methods_and_attributes.cpp
test_modules.cpp test_modules.cpp
......
/*
tests/test_alias_initialization.cpp -- test cases and example of different trampoline
initialization modes
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, Jason Rhinelander <jason@imaginary.ca>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
test_initializer alias_initialization([](py::module &m) {
// don't invoke Python dispatch classes by default when instantiating C++ classes that were not
// extended on the Python side
struct A {
virtual ~A() {}
virtual void f() { py::print("A.f()"); }
};
struct PyA : A {
PyA() { py::print("PyA.PyA()"); }
~PyA() { py::print("PyA.~PyA()"); }
void f() override {
py::print("PyA.f()");
PYBIND11_OVERLOAD(void, A, f);
}
};
auto call_f = [](A *a) { a->f(); };
py::class_<A, PyA>(m, "A")
.def(py::init<>())
.def("f", &A::f);
m.def("call_f", call_f);
// ... unless we explicitly request it, as in this example:
struct A2 {
virtual ~A2() {}
virtual void f() { py::print("A2.f()"); }
};
struct PyA2 : A2 {
PyA2() { py::print("PyA2.PyA2()"); }
~PyA2() { py::print("PyA2.~PyA2()"); }
void f() override {
py::print("PyA2.f()");
PYBIND11_OVERLOAD(void, A2, f);
}
};
py::class_<A2, PyA2>(m, "A2")
.def(py::init_alias<>())
.def("f", &A2::f);
m.def("call_f", [](A2 *a2) { a2->f(); });
});
import pytest
def test_alias_delay_initialization1(capture):
"""
A only initializes its trampoline class when we inherit from it; if we just
create and use an A instance directly, the trampoline initialization is
bypassed and we only initialize an A() instead (for performance reasons).
"""
from pybind11_tests import A, call_f
class B(A):
def __init__(self):
super(B, self).__init__()
def f(self):
print("In python f()")
# C++ version
with capture:
a = A()
call_f(a)
del a
pytest.gc_collect()
assert capture == "A.f()"
# Python version
with capture:
b = B()
call_f(b)
del b
pytest.gc_collect()
assert capture == """
PyA.PyA()
PyA.f()
In python f()
PyA.~PyA()
"""
def test_alias_delay_initialization2(capture):
"""A2, unlike the above, is configured to always initialize the alias; while
the extra initialization and extra class layer has small virtual dispatch
performance penalty, it also allows us to do more things with the trampoline
class such as defining local variables and performing construction/destruction.
"""
from pybind11_tests import A2, call_f
class B2(A2):
def __init__(self):
super(B2, self).__init__()
def f(self):
print("In python B2.f()")
# No python subclass version
with capture:
a2 = A2()
call_f(a2)
del a2
pytest.gc_collect()
assert capture == """
PyA2.PyA2()
PyA2.f()
A2.f()
PyA2.~PyA2()
"""
# Python subclass version
with capture:
b2 = B2()
call_f(b2)
del b2
pytest.gc_collect()
assert capture == """
PyA2.PyA2()
PyA2.f()
In python B2.f()
PyA2.~PyA2()
"""
...@@ -23,6 +23,133 @@ TEST_SUBMODULE(class_, m) { ...@@ -23,6 +23,133 @@ TEST_SUBMODULE(class_, m) {
py::class_<NoConstructor>(m, "NoConstructor") py::class_<NoConstructor>(m, "NoConstructor")
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance"); .def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
// test_inheritance
class Pet {
public:
Pet(const std::string &name, const std::string &species)
: m_name(name), m_species(species) {}
std::string name() const { return m_name; }
std::string species() const { return m_species; }
private:
std::string m_name;
std::string m_species;
};
class Dog : public Pet {
public:
Dog(const std::string &name) : Pet(name, "dog") {}
std::string bark() const { return "Woof!"; }
};
class Rabbit : public Pet {
public:
Rabbit(const std::string &name) : Pet(name, "parrot") {}
};
class Hamster : public Pet {
public:
Hamster(const std::string &name) : Pet(name, "rodent") {}
};
class Chimera : public Pet {
Chimera() : Pet("Kimmy", "chimera") {}
};
py::class_<Pet> pet_class(m, "Pet");
pet_class
.def(py::init<std::string, std::string>())
.def("name", &Pet::name)
.def("species", &Pet::species);
/* One way of declaring a subclass relationship: reference parent's class_ object */
py::class_<Dog>(m, "Dog", pet_class)
.def(py::init<std::string>());
/* Another way of declaring a subclass relationship: reference parent's C++ type */
py::class_<Rabbit, Pet>(m, "Rabbit")
.def(py::init<std::string>());
/* And another: list parent in class template arguments */
py::class_<Hamster, Pet>(m, "Hamster")
.def(py::init<std::string>());
/* Constructors are not inherited by default */
py::class_<Chimera, Pet>(m, "Chimera");
m.def("pet_name_species", [](const Pet &pet) { return pet.name() + " is a " + pet.species(); });
m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
// test_automatic_upcasting
struct BaseClass { virtual ~BaseClass() {} };
struct DerivedClass1 : BaseClass { };
struct DerivedClass2 : BaseClass { };
py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
m.def("return_class_n", [](int n) -> BaseClass* {
if (n == 1) return new DerivedClass1();
if (n == 2) return new DerivedClass2();
return new BaseClass();
});
m.def("return_none", []() -> BaseClass* { return nullptr; });
// test_isinstance
m.def("check_instances", [](py::list l) {
return py::make_tuple(
py::isinstance<py::tuple>(l[0]),
py::isinstance<py::dict>(l[1]),
py::isinstance<Pet>(l[2]),
py::isinstance<Pet>(l[3]),
py::isinstance<Dog>(l[4]),
py::isinstance<Rabbit>(l[5]),
py::isinstance<UnregisteredType>(l[6])
);
});
// test_mismatched_holder
struct MismatchBase1 { };
struct MismatchDerived1 : MismatchBase1 { };
struct MismatchBase2 { };
struct MismatchDerived2 : MismatchBase2 { };
m.def("mismatched_holder_1", []() {
auto mod = py::module::import("__main__");
py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(mod, "MismatchBase1");
py::class_<MismatchDerived1, MismatchBase1>(mod, "MismatchDerived1");
});
m.def("mismatched_holder_2", []() {
auto mod = py::module::import("__main__");
py::class_<MismatchBase2>(mod, "MismatchBase2");
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>,
MismatchBase2>(mod, "MismatchDerived2");
});
// test_override_static
// #511: problem with inheritance + overwritten def_static
struct MyBase {
static std::unique_ptr<MyBase> make() {
return std::unique_ptr<MyBase>(new MyBase());
}
};
struct MyDerived : MyBase {
static std::unique_ptr<MyDerived> make() {
return std::unique_ptr<MyDerived>(new MyDerived());
}
};
py::class_<MyBase>(m, "MyBase")
.def_static("make", &MyBase::make);
py::class_<MyDerived, MyBase>(m, "MyDerived")
.def_static("make", &MyDerived::make)
.def_static("make2", &MyDerived::make);
} }
template <int N> class BreaksBase {}; template <int N> class BreaksBase {};
......
...@@ -42,3 +42,80 @@ def test_docstrings(doc): ...@@ -42,3 +42,80 @@ def test_docstrings(doc):
Return an instance Return an instance
""" """
def test_inheritance(msg):
roger = m.Rabbit('Rabbit')
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
assert m.pet_name_species(roger) == "Rabbit is a parrot"
polly = m.Pet('Polly', 'parrot')
assert polly.name() + " is a " + polly.species() == "Polly is a parrot"
assert m.pet_name_species(polly) == "Polly is a parrot"
molly = m.Dog('Molly')
assert molly.name() + " is a " + molly.species() == "Molly is a dog"
assert m.pet_name_species(molly) == "Molly is a dog"
fred = m.Hamster('Fred')
assert fred.name() + " is a " + fred.species() == "Fred is a rodent"
assert m.dog_bark(molly) == "Woof!"
with pytest.raises(TypeError) as excinfo:
m.dog_bark(polly)
assert msg(excinfo.value) == """
dog_bark(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.class_.Dog) -> str
Invoked with: <m.class_.Pet object at 0>
"""
with pytest.raises(TypeError) as excinfo:
m.Chimera("lion", "goat")
assert "No constructor defined!" in str(excinfo.value)
def test_automatic_upcasting():
assert type(m.return_class_1()).__name__ == "DerivedClass1"
assert type(m.return_class_2()).__name__ == "DerivedClass2"
assert type(m.return_none()).__name__ == "NoneType"
# Repeat these a few times in a random order to ensure no invalid caching is applied
assert type(m.return_class_n(1)).__name__ == "DerivedClass1"
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
assert type(m.return_class_n(0)).__name__ == "BaseClass"
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
assert type(m.return_class_n(0)).__name__ == "BaseClass"
assert type(m.return_class_n(1)).__name__ == "DerivedClass1"
def test_isinstance():
objects = [tuple(), dict(), m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4
expected = (True, True, True, True, True, False, False)
assert m.check_instances(objects) == expected
def test_mismatched_holder():
import re
with pytest.raises(RuntimeError) as excinfo:
m.mismatched_holder_1()
assert re.match('generic_type: type ".*MismatchDerived1" does not have a non-default '
'holder type while its base ".*MismatchBase1" does', str(excinfo.value))
with pytest.raises(RuntimeError) as excinfo:
m.mismatched_holder_2()
assert re.match('generic_type: type ".*MismatchDerived2" has a non-default holder type '
'while its base ".*MismatchBase2" does not', str(excinfo.value))
def test_override_static():
"""#511: problem with inheritance + overwritten def_static"""
b = m.MyBase.make()
d1 = m.MyDerived.make2()
d2 = m.MyDerived.make()
assert isinstance(b, m.MyBase)
assert isinstance(d1, m.MyDerived)
assert isinstance(d2, m.MyDerived)
/*
tests/test_inheritance.cpp -- inheritance, automatic upcasting for polymorphic types
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
class Pet {
public:
Pet(const std::string &name, const std::string &species)
: m_name(name), m_species(species) {}
std::string name() const { return m_name; }
std::string species() const { return m_species; }
private:
std::string m_name;
std::string m_species;
};
class Dog : public Pet {
public:
Dog(const std::string &name) : Pet(name, "dog") {}
std::string bark() const { return "Woof!"; }
};
class Rabbit : public Pet {
public:
Rabbit(const std::string &name) : Pet(name, "parrot") {}
};
class Hamster : public Pet {
public:
Hamster(const std::string &name) : Pet(name, "rodent") {}
};
class Chimera : public Pet {
Chimera() : Pet("Kimmy", "chimera") {}
};
std::string pet_name_species(const Pet &pet) {
return pet.name() + " is a " + pet.species();
}
std::string dog_bark(const Dog &dog) {
return dog.bark();
}
struct BaseClass { virtual ~BaseClass() {} };
struct DerivedClass1 : BaseClass { };
struct DerivedClass2 : BaseClass { };
struct MismatchBase1 { };
struct MismatchDerived1 : MismatchBase1 { };
struct MismatchBase2 { };
struct MismatchDerived2 : MismatchBase2 { };
test_initializer inheritance([](py::module &m) {
py::class_<Pet> pet_class(m, "Pet");
pet_class
.def(py::init<std::string, std::string>())
.def("name", &Pet::name)
.def("species", &Pet::species);
/* One way of declaring a subclass relationship: reference parent's class_ object */
py::class_<Dog>(m, "Dog", pet_class)
.def(py::init<std::string>());
/* Another way of declaring a subclass relationship: reference parent's C++ type */
py::class_<Rabbit, Pet>(m, "Rabbit")
.def(py::init<std::string>());
/* And another: list parent in class template arguments */
py::class_<Hamster, Pet>(m, "Hamster")
.def(py::init<std::string>());
py::class_<Chimera, Pet>(m, "Chimera");
m.def("pet_name_species", pet_name_species);
m.def("dog_bark", dog_bark);
py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
m.def("return_class_n", [](int n) -> BaseClass* {
if (n == 1) return new DerivedClass1();
if (n == 2) return new DerivedClass2();
return new BaseClass();
});
m.def("return_none", []() -> BaseClass* { return nullptr; });
m.def("test_isinstance", [](py::list l) {
return py::make_tuple(
py::isinstance<py::tuple>(l[0]),
py::isinstance<py::dict>(l[1]),
py::isinstance<Pet>(l[2]),
py::isinstance<Pet>(l[3]),
py::isinstance<Dog>(l[4]),
py::isinstance<Rabbit>(l[5]),
py::isinstance<UnregisteredType>(l[6])
);
});
m.def("test_mismatched_holder_type_1", []() {
auto m = py::module::import("__main__");
py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(m, "MismatchBase1");
py::class_<MismatchDerived1, MismatchBase1>(m, "MismatchDerived1");
});
m.def("test_mismatched_holder_type_2", []() {
auto m = py::module::import("__main__");
py::class_<MismatchBase2>(m, "MismatchBase2");
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(m, "MismatchDerived2");
});
// #511: problem with inheritance + overwritten def_static
struct MyBase {
static std::unique_ptr<MyBase> make() {
return std::unique_ptr<MyBase>(new MyBase());
}
};
struct MyDerived : MyBase {
static std::unique_ptr<MyDerived> make() {
return std::unique_ptr<MyDerived>(new MyDerived());
}
};
py::class_<MyBase>(m, "MyBase")
.def_static("make", &MyBase::make);
py::class_<MyDerived, MyBase>(m, "MyDerived")
.def_static("make", &MyDerived::make)
.def_static("make2", &MyDerived::make);
});
import pytest
def test_inheritance(msg):
from pybind11_tests import Pet, Dog, Rabbit, Hamster, Chimera, dog_bark, pet_name_species
roger = Rabbit('Rabbit')
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
assert pet_name_species(roger) == "Rabbit is a parrot"
polly = Pet('Polly', 'parrot')
assert polly.name() + " is a " + polly.species() == "Polly is a parrot"
assert pet_name_species(polly) == "Polly is a parrot"
molly = Dog('Molly')
assert molly.name() + " is a " + molly.species() == "Molly is a dog"
assert pet_name_species(molly) == "Molly is a dog"
fred = Hamster('Fred')
assert fred.name() + " is a " + fred.species() == "Fred is a rodent"
assert dog_bark(molly) == "Woof!"
with pytest.raises(TypeError) as excinfo:
dog_bark(polly)
assert msg(excinfo.value) == """
dog_bark(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.Dog) -> str
Invoked with: <m.Pet object at 0>
"""
with pytest.raises(TypeError) as excinfo:
Chimera("lion", "goat")
assert "No constructor defined!" in str(excinfo.value)
def test_automatic_upcasting():
from pybind11_tests import return_class_1, return_class_2, return_class_n, return_none
assert type(return_class_1()).__name__ == "DerivedClass1"
assert type(return_class_2()).__name__ == "DerivedClass2"
assert type(return_none()).__name__ == "NoneType"
# Repeat these a few times in a random order to ensure no invalid caching
# is applied
assert type(return_class_n(1)).__name__ == "DerivedClass1"
assert type(return_class_n(2)).__name__ == "DerivedClass2"
assert type(return_class_n(0)).__name__ == "BaseClass"
assert type(return_class_n(2)).__name__ == "DerivedClass2"
assert type(return_class_n(2)).__name__ == "DerivedClass2"
assert type(return_class_n(0)).__name__ == "BaseClass"
assert type(return_class_n(1)).__name__ == "DerivedClass1"
def test_isinstance():
from pybind11_tests import test_isinstance, Pet, Dog
objects = [tuple(), dict(), Pet("Polly", "parrot")] + [Dog("Molly")] * 4
expected = (True, True, True, True, True, False, False)
assert test_isinstance(objects) == expected
def test_holder():
from pybind11_tests import test_mismatched_holder_type_1, test_mismatched_holder_type_2
with pytest.raises(RuntimeError) as excinfo:
test_mismatched_holder_type_1()
assert str(excinfo.value) == ("generic_type: type \"MismatchDerived1\" does not have "
"a non-default holder type while its base "
"\"MismatchBase1\" does")
with pytest.raises(RuntimeError) as excinfo:
test_mismatched_holder_type_2()
assert str(excinfo.value) == ("generic_type: type \"MismatchDerived2\" has a "
"non-default holder type while its base "
"\"MismatchBase2\" does not")
def test_inheritance_override_def_static():
"""#511: problem with inheritance + overwritten def_static"""
from pybind11_tests import MyBase, MyDerived
b = MyBase.make()
d1 = MyDerived.make2()
d2 = MyDerived.make()
assert isinstance(b, MyBase)
assert isinstance(d1, MyDerived)
assert isinstance(d2, MyDerived)
...@@ -322,7 +322,7 @@ struct DispatchIssue : Base { ...@@ -322,7 +322,7 @@ struct DispatchIssue : Base {
} }
}; };
test_initializer virtual_functions([](py::module &m) { TEST_SUBMODULE(virtual_functions, m) {
py::class_<ExampleVirt, PyExampleVirt>(m, "ExampleVirt") py::class_<ExampleVirt, PyExampleVirt>(m, "ExampleVirt")
.def(py::init<int>()) .def(py::init<int>())
/* Reference original class in function definitions */ /* Reference original class in function definitions */
...@@ -352,6 +352,52 @@ test_initializer virtual_functions([](py::module &m) { ...@@ -352,6 +352,52 @@ test_initializer virtual_functions([](py::module &m) {
m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>); m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>);
initialize_inherited_virtuals(m); initialize_inherited_virtuals(m);
// test_alias_delay_initialization1
// don't invoke Python dispatch classes by default when instantiating C++ classes
// that were not extended on the Python side
struct A {
virtual ~A() {}
virtual void f() { py::print("A.f()"); }
};
struct PyA : A {
PyA() { py::print("PyA.PyA()"); }
~PyA() { py::print("PyA.~PyA()"); }
void f() override {
py::print("PyA.f()");
PYBIND11_OVERLOAD(void, A, f);
}
};
py::class_<A, PyA>(m, "A")
.def(py::init<>())
.def("f", &A::f);
m.def("call_f", [](A *a) { a->f(); });
// test_alias_delay_initialization2
// ... unless we explicitly request it, as in this example:
struct A2 {
virtual ~A2() {}
virtual void f() { py::print("A2.f()"); }
};
struct PyA2 : A2 {
PyA2() { py::print("PyA2.PyA2()"); }
~PyA2() { py::print("PyA2.~PyA2()"); }
void f() override {
py::print("PyA2.f()");
PYBIND11_OVERLOAD(void, A2, f);
}
};
py::class_<A2, PyA2>(m, "A2")
.def(py::init_alias<>())
.def("f", &A2::f);
m.def("call_f", [](A2 *a2) { a2->f(); });
// #159: virtual function dispatch has problems with similar-named functions // #159: virtual function dispatch has problems with similar-named functions
py::class_<Base, DispatchIssue>(m, "DispatchIssue") py::class_<Base, DispatchIssue>(m, "DispatchIssue")
.def(py::init<>()) .def(py::init<>())
...@@ -398,4 +444,4 @@ test_initializer virtual_functions([](py::module &m) { ...@@ -398,4 +444,4 @@ test_initializer virtual_functions([](py::module &m) {
// .def("str_ref", &OverrideTest::str_ref) // .def("str_ref", &OverrideTest::str_ref)
.def("A_value", &OverrideTest::A_value) .def("A_value", &OverrideTest::A_value)
.def("A_ref", &OverrideTest::A_ref); .def("A_ref", &OverrideTest::A_ref);
}); }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment